简体   繁体   中英

Calling then before or after Promise.all

Currently I have following code:

var detailPromises = links.map(function (link) { return requestP(link) });

var oldPollNames = [];
// add a database call to the promises as it also can resolved during detail fetching
detailPromises.push(db.polls.findAsync({}, {title: 1}));
return Promise.all(detailPromises)
    .then(function (data) {
        oldPollNames = data.pop().map(function (item) { return item.title; });
        return data;
    })
    .then(function (htmls) {
        var newPolls = [];
        htmls.forEach(function (i, html) {
            // some processing of html using oldPollNames (newPools.push...)
        });
        return newPolls;
    })
    .then(/* insert into db */);

What I'm doing is: waiting for db + waiting for html requests and then process booth.

I'm wondering if it would make more sense / be more performant to do following:

var detailPromises = links.map(function (link) { return requestP(link) });

return db.polls.findAsync({}, {title: 1}).then(function(polls) {
    var oldPollNames = polls.map(function (item) { return item.title; });
    var newPolls = [];

    detailPromises = detailPromises.map(function (p) {
        return p.then(function (html) {
            // some processing of html using oldPollNames (newPools.push...)
        })
    });

    return Promise.all(detailPromises)
        .done(function () {
            // insert newPolls into db
        });
});

The advantage of the second approach imo is that each request gets (already) handled when it is done and can do it's procesing before all / other promises are fulfilled.

I'm wondering if it would make more sense / be more performant

Yes, it would be more performant to process each html request separately and as fast as possible, instead of waiting for all of them and then processing them together in a huge loop. Not only will they processed earlier, you also avoid a possibly long-running loop of heavy processing.

However, the approach also has its drawbacks: it's more complicated to implement. The code you've given is prone to reporting Unhandled Rejection s if any of the detailPromises rejects before the database query fulfills, or if any of them rejects and the database query is rejected as well. To prevent that, you'll need to use Promise.all on all the promises anyway:

var detailPromises = links.map(requestP);

var resultPromise = db.polls.findAsync({}, {title: 1}).then(function(polls) {
    var oldPollNames = polls.map(function(item) { return item.title; });
    var newPollPromises = detailPromises.map(function(p, i) {
        return p.then(function(html) {
            // some processing of html
        });
    });

    return Promise.all(newPollPromies) // not the detailPromises!
    .then(function(newPolls) {
        // insert newPolls into db
    });
});
return Promise.all([resultPromise].concat(detailPromises)).then(function(r) {
    return r[0];
});

In addition to Bergi's answer I think I found another likewise solution:

var detailPromises = links.map(requestP);
var pollNamePromise = db.polls.findAsync({}, {title: 1}).then(function(polls) {
        return polls.map(function (item) { return item.title; });
    });

var newPromises = detailPromises.map(function(p) {
        return Promise.join(p, pollNamePromise, function(html, oldPollNames) {
            // some processing of html using oldPollNames (newPools.push...)
        });
    });

Promise.all(newPromises).then(function(newPolls) {
    // insert newPolls into db
});

Edit Changed from Promise.all to Promise.join which is available in Bluebird

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM