Petah
Petah

Reputation: 46060

How do you avoid the promise constructor antipattern with Promise.all

How do you avoid the promise constructor antipattern when using multiple promises and Promise.all?

Say I have the following code:

getFoo = function() {
    return new Promise(function(resolve, reject) {
        var promises = [];
        promises.push(new Promise(function(resolve, reject) => {
            getBar1().then(function(bar1) {
                processBar1(bar1); 
                resolve(bar1);
            });
        }));
        promises.push(new Promise(function(resolve, reject) => {
            getBar2().then(function(bar2) {
                processBar2(bar2); 
                resolve(bar2);
            });
        }));
        Promise.all(promises).spread(function(bar1, bar2) {
            var result = processBothBars(bar1, bar2);
            resolve(result);
        });
    });
}

It presents some of the fundamental issues of the antipattern, errors get swallowed up, and pyramids of doom.

I am using bluebird BTW.

Upvotes: 3

Views: 316

Answers (3)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276506

Fwiw bluebird proves some sugar over this:

getFoo = function() {
     return Promise.join(getBar1().tap(processBar1),
                         getBar2().tap(processBar2),
                         processBothBars);
}

Upvotes: 2

Roamer-1888
Roamer-1888

Reputation: 19288

No need to create any promises of your own here because getBar1() and getBar2() both already return promises - at least we assume so because both are thenable.

Providing processBar1 and processBar2 each returns the result you are interested in, the code will simplify as follows :

var getFoo = function() {
    // write promises as an array literal
    var promises = [
        getBar1().then(processBar1),//result returned by getBar1() is automatically passed to processBar1
        getBar2().then(processBar2) // ... ditto ...
    ];
    return Promise.all(promises).spread(processBothBars);
};

Upvotes: 2

Kevin B
Kevin B

Reputation: 95066

You can just get rid of new Promise all together.

getFoo = function() {
    var promises = [];
    promises.push(getBar1().then(function(bar1) {
        processBar1(bar1);
        return bar1;
    }));
    promises.push(getBar2().then(function(bar2) {
        processBar2(bar2);
        return bar2;
    }));
    return Promise.all(promises).spread(function(bar1, bar2) {
        var result = processBothBars(bar1, bar2);
        return result;
    });
}

// start mock
function getBar1() {
    return Promise.resolve({name:'bar1',processed: false});
}
function getBar2() {
    return Promise.resolve({name:'bar2',processed: false});
}
function processBar1(bar1) {
  bar1.processed = true;
}
function processBar2(bar2) {
  bar2.processed = true;
}
function processBothBars (bar1, bar2) {
  return [bar1, bar2].filter(function (bar) {
    return bar.processed;
  }).map(function (bar) {
    return bar.name;
  });
}
Promise.prototype.spread = function (fn) {
  return this.then(function (arr) {
      return fn.apply(this, arr);
  });
};
// end mock

var getFoo = function (fail) {
    var promises = [];
    promises.push(getBar1().then(function (bar1) {
        processBar1(bar1);
        if (fail) {
          throw 'getBar1 Failed!';
        }
        return bar1;
    }));
    promises.push(getBar2().then(function (bar2) {
        processBar2(bar2);
        return bar2;
    }));
    return Promise.all(promises).spread(function (bar1, bar2) {
        var result = processBothBars(bar1, bar2);
        return result;
    });
}
getFoo().then(function (result) {
    console.log(result); // ['bar1', 'bar2']
});
getFoo(true).then(function (result) {
    console.log(result); // doesn't happen
}).catch(function (e) {
    console.error(e); // Error: getBar1 Failed!
});

.then returns a promise, so there's no need to create a new one that wraps it unless you want to prevent errors from reaching the outer promise.

Upvotes: 4

Related Questions