1

I am looking for something similar to Promise.all that will continue to resolve promises concurrently even in the event that one or more of the promises reject or throw an error. Each request does not rely on another request.

Close to what I want - please see comments

function fetchRequest (request) {
  return new Promise(function (resolve, reject) {
    fetch(request)
    .then(function(response) {
      return response.text();      

    }).then(function (responseXML) {
      //Do something here. Maybe add data to dom
      resolve(responseXML);

    }).catch(function (err) {
      reject(new Error(err));
    }
}

function promiseRequests (requests) {
  var result = Promise.resolve();

  for (var i = 0; i < requests.length; i++) {
    result = fetchRequest(requests[i])
  }

  //This is wrong as it will resolve when the last promise in the requests array resolves
  // - not when all requests resolve
  resolve(result);
}

promiseRequests(['url1.com', 'url2.com']).then(function (data) {
  console.log('All requests finished');
  //optionally have data be an array of resolved and rejected promises
});

I have succeeding in using Promise.all together with only ever resolving the fetchRequest promise and this results in the expected outcome (an array of results and undefined's) but I feel like this is the wrong way to do things. It also removes my ability to use thrown errors.

Works but feels like incorrect use of resolve

function fetchRequest (request) {
  return new Promise(function (resolve, reject) {
    fetch(request)
    .then(function(response) {
      return response.text();      

    }).then(function (responseXML) {
      resolve(responseXML);

    }).catch(function (err) {
      resolve();
    }
}

Promise.all([fetchRequest('url1.com'), fetchRequest('url2.com')]).then(function (data) {
  console.log('All requests finished', data); //data could be ['resultXML', undefined]
});

Please only native es6 promise API answers thanks.

Calummm
  • 819
  • 1
  • 8
  • 21
  • 3
    First of all, avoid the [promise constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Jun 19 '15 at 05:09
  • 1
    "*It also removes my ability to use thrown errors.*" - then why not just `resolve(err)`? How do you intend to use them? How would you like to distinguish between fulfillments and rejections? – Bergi Jun 19 '15 at 05:11
  • Ah I see. After reading your link Bergi I think my understanding of promises has finally clicked. I think Domenic's answer treats the symptom of my problem but Bergi's answer fixes the root cause. – Calummm Jun 19 '15 at 06:59

2 Answers2

6

I have succeeded in using Promise.all together with only ever resolving the fetchRequest promises

That's basically the way to go. ES6 does not have a helper function like allSettled (Q) or settle (Bluebird 2.x) for this case, so we will need to use Promise.all similar to like you did. Bluebird even has a dedicated .reflect() utility for this.

You would however not resolve them with undefined in the case of a rejection, but rather with some useful value that allows to identify the errors.

function promiseRequests(requests) {
  return Promise.all(requests.map(request => {
    return fetch(request).then(res => {
      return {value:res};
    }, err => {
      return {reason:err};
    });
  }));
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • See `))` at close of `Promise.all()` . Question: Does this pattern handle "The Overly Keen Error Handler" case described at http://taoofcode.net/promise-anti-patterns/ ? – guest271314 Jun 19 '15 at 07:11
  • @guest271314: No need to "handle" it - [this usage is not an antipattern](http://stackoverflow.com/a/24663315/1048572) (yes I know what I'm doing :-) – Bergi Jun 19 '15 at 07:15
  • Note, Without `return` within `.map()` , at `fetch` , `promiseRequests` appear to return `undefined` for `Promise` values https://jsfiddle.net/gn81Lf6u/2/ , https://jsfiddle.net/gn81Lf6u/3/ – guest271314 Jun 19 '15 at 07:31
  • 1
    @guest271314: It's an arrow function, where the return is implicit :-) But I've edited for clarity (and consistency with the other two expressions where no concisebody is possible) – Bergi Jun 19 '15 at 07:35
  • _"It's an arrow function, where the return is implicit :-)"_ Tried arrow function at nightly without `return` before `fetch` ; appeared to return `undefined` for `Promise` values from `promiseRequests` https://jsfiddle.net/gn81Lf6u/5/ , https://jsfiddle.net/gn81Lf6u/7/ – guest271314 Jun 19 '15 at 07:59
  • @guest271314: You must not use `{}` braces if you want implicit returns. It does only work for concise bodies, not for function bodies. – Bergi Jun 19 '15 at 08:03
  • Tried, read MDN page on arrow function, though have not been able to return same results from `.map()` without curly braces . If question is not too far removed from original Question, can provide example using `.map` without `{}` to return same result ? – guest271314 Jun 19 '15 at 14:38
  • @guest271314: Trivial example: `[1,2,3].map(x => x+1)`. Should yield numbers. Maybe FF or Chrome are not yet supporting this, I don't know, but it's the standard sytax. Yes, for further questions you should consider [posting them](http://stackoverflow.com/questions/ask) :-) – Bergi Jun 19 '15 at 18:34
4

You are essentially asking for a way to swallow any errors. As such, a function like this will be your best bet:

function swallow(p) {
  // transforms rejected promises into promises fulfilled with undefined
  return p.catch(function () { });
}

You would use it as follows:

Promise.all([swallow(fetch('url1.com')), swallow(fetch('url2.com'))]).then(function (data) {
  console.log('All requests finished', data); //data could be ['resultXML', undefined]
});

or even

 const promises = ['url1.com', 'url2.com'].map(fetch).map(swallow);
 Promise.all(promises).then(function (data) {
   // ...
 });
Domenic
  • 110,262
  • 41
  • 219
  • 271