John B.
John B.

Reputation: 2359

Promise return value and nesting

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

Answers (1)

John B.
John B.

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

Related Questions