Reputation: 2359
I have a function createOne()
that should try to find a MongoDB document or create it if it cannot be found. In either way, a promise should be returned and the promise resolved with doc
:
var createOne = function(category) {
var self = this;
return this.findOne({ slug: category.slug }).exec()
.then(function(doc) {
if (!doc) {
return self.create(category); // wait to resolve until document created.
}
});
};
Both findOne().exec()
and create()
(should) return a promise.
I tried out many different ways such as using Q.fcall, manually creating one with Q.defer() and others, but either the resolve value was missing, or the second { slug: 'foo' }
was created as well even though the first already existed(?).
Following is my calling code:
var data = [
{ slug: 'foo' },
{ slug: 'bar' },
{ slug: 'foo' } // <- shouldn't be created because of first 'foo'.
];
Q.fcall(function() {
// [...]
}).then(function() {
var promises = data.map(function(category) {
return createOne(category); // <- calls createOne().
});
return Q.all(promises);
}).then(function(categories) {
console.log(categories);
}).done();
How can I structure createOne()
so that console.log(categories)
returns the documents, whether they are found or created first?
Edit: When the collection is empty, only two documents should be created. { slug: 'foo' }
only once.
Upvotes: 1
Views: 1031
Reputation: 2359
It seems I have figured out how to call asynchronous functions sequentially and compose a final array of all their promises (and later resolved values).
My solution is based on a great array.reduce
example I found in a comment from kriskowal on Github.
var data = [
'Element 1', 'Element 2', 'Element 3'
];
function asyncMock(element) {
console.log('Handle', element);
return Q.delay(element + ' handled.', 500);
}
var promise = data.reduce(function (results, element) {
return results.then(function (results) {
return asyncMock(element).then(function(result) {
results.push(result);
return results;
});
});
}, Q([]));
promise.then(function(elements) {
console.log('Finished:', elements);
}).done();
You can find a working demo here: http://jsfiddle.net/Lnvu4/
Upvotes: 1