Chain async fallbacks and print their errors only if all fail

I want to invoke an async method, followed by a series of async fallback methods until one of them succeeds.
If all invocations fail, then I want all of their errors printed. Otherwise, if even one succeeds, the errors should not be printed.
This is what I want:

tryX()
 .catch(x => tryXFallback1()
  .catch(xf1 => tryXFallback2()
   .catch(xf2 => tryXFallback3()
    .catch(xf3 => tryXFallback4()
     // ...
     .catch(xf4 => Promise.reject([x, xf1, xf2, xf3, xf4]))))));

But I’m not a fan of the indentation. Accumulating the errors in a variable outside the scope of the catch clauses also seems messy:

let errors = [];
tryX()
    .catch(x => {
        errors.push(x);
        return tryXFallback1();
    })
    .catch(xf1 => {
        errors.push(x);
        return tryXFallback2();
    })
    .catch(xf2 => {
        errors.push(x);
        return tryXFallback3();
    })
    .catch(xf3 => {
        errors.push(x);
        return tryXFallback4();
    })
    // ...
    .catch(xf4 => Promise.reject(errors));

Lastly, I thought I could do some sort of for loop instead but that seems even uglier e.g.:

let methods = [tryX, tryFallback1, tryFallback2, tryFallback3, tryFallback4, /*...*/];
let errors = [];
for (let x of methods)
    try {
        return await x();
    } catch (e) {
        errors.push(e);
    }
if (errors.length === methods.length)
    return Promise.reject(errors);

Does anyone know of a more elegant approach?

19 thoughts on “Chain async fallbacks and print their errors only if all fail”

Leave a Comment