peter flanagan
peter flanagan

Reputation: 9790

only throw error if all API calls in Promise.all fail

Before starting an express server JS I want to make three API calls. If any one of them fails I just want to log an error, but if all three fail I want to throw an error and prevent the server from starting.

I have seen I can use Promise.all, but I am unsure how to handle the case if one fails. Using the code below if any fail the error will be thrown. How can I limit this to only occurring if all calls fail?

const fetchNames = async () => {
    try {
      await Promise.all([
        axios.get("./one.json"),
        axios.get("./two.json"),
        axios.get("./three.json")
      ]);
    } catch {
      throw Error("Promise failed");
    }
  };

Upvotes: 0

Views: 994

Answers (3)

Bergi
Bergi

Reputation: 664217

If you need just any of the results, Promise.any will work for this use case - it'll reject only if all promises reject.

const value = await Promise.any([
    axios.get("./one.json").catch(err => { console.log(err); throw err; }),
    axios.get("./two.json").catch(err => { console.log(err); throw err; }),
    axios.get("./three.json").catch(err => { console.log(err); throw err; }),
]);

If you need all result values from the promises that did happen to fulfill, use Promise.allSettled.

const results = await Promise.allSettled([
    axios.get("./one.json"),
    axios.get("./two.json"),
    axios.get("./three.json"),
]);
const values = [], errors = [];
for (const result of results) {
    if (result.status === 'fulfilled') {
        values.push(result.value);
    } else { // result.status === 'rejected'
        errors.push(result.reason);
    }
}
if (!values.length) {
    throw new AggregateError(errors);
} else {
    for (const err of errors) {
        console.log(err);
    }
    // do stuff with values
}

Upvotes: 1

Tudor Constantin
Tudor Constantin

Reputation: 26861

If I understand correctly, you're actually interested in not having the catch(e){...} executed if any of them worked, right? then you can do this:

const fetchNames = async () => {
    try {
      await Promise.all([
        axios.get("./one.json").catch(e => console.log(`one failed`, e)),
        axios.get("./two.json").catch(e => console.log(`two failed`, e)),
        axios.get("./three.json").catch(e => console.log(`three failed`, e))
      ]);
    } catch {
      throw Error("Promise failed");
    }
  };

The problem above is that if all of them fail, then no error is thrown. If you're also interested in that, then something like this should work:

const fetchNames = async () => {
    try {
      let success = false;
      await Promise.all([
        axios.get("./one.json").then( () => success = true).catch(e => console.log(`one failed`, e)),
        axios.get("./two.json").then( () => success = true).catch(e => console.log(`two failed`, e)),
        axios.get("./three.json").then( () => success = true).catch(e => console.log(`three failed`, e))
      ]);
    if (!success) throw new Error(`No successful promises`);
    } catch {
      throw Error("Promise failed");
    }
  };

Upvotes: -1

CertainPerformance
CertainPerformance

Reputation: 370659

If you don't need the fulfillment values, or need just any of them, Promise.any will work for this use case - it'll reject only if all Promises reject.

const firstResolveValue = await Promise.any([
        axios.get("./one.json"),
        axios.get("./two.json"),
        axios.get("./three.json")
]);

If you need all result values from the Promises that happen to fulfill, use Promise.allSettled.

const settledResults = await Promise.allSettled([
    axios.get("./one.json"),
    axios.get("./two.json"),
    axios.get("./three.json")
]);
const fulfilledResults = settledResults.filter(result => result.status === 'fulfilled');
if (!fulfilledResults.length) {
    throw new Error();
} else {
    // do stuff with fulfilledResults
}

Upvotes: 3

Related Questions