Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
168 views
in Technique[技术] by (71.8m points)

javascript - 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?

question from:https://stackoverflow.com/questions/65924209/chain-async-fallbacks-and-print-their-errors-only-if-all-fail

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The loop you have seems fine. I would probably stick with it as it already works. However, here is an alternative:

function tryWithFallbacks(main, ...fallbacks) {
  return fallbacks.reduce(
    (p, nextFallback) => p.catch( //handle errors
      err => nextFallback() //try using the fallback
        .catch(e => Promise.reject(err.concat(e))) //propagate rejection reasons
                                                   //on failure by adding to 
                                                   //the array of errors
    ), 
    main() //seed the process with the main task
      .catch(err => Promise.reject([err])) //ensure there is an array of errors
  );
}

const a = tryWithFallbacks(
  () => Promise.resolve(42)
);
test(a, "a"); //42

const b = tryWithFallbacks(
  () => Promise.reject("oops"),
  () => Promise.resolve("it's fine")
);
test(b, "b"); //"it's fine"

const c = tryWithFallbacks(
  () => Promise.reject("oops1"),
  () => Promise.reject("oops2"),
  () => Promise.reject("oops3")
);
test(c, "c"); //["oops1", "oops2", "oops3"]

const d = tryWithFallbacks(
  () => Promise.reject("oops1"),
  () => Promise.reject("oops2"),
  () => Promise.reject("oops3"),
  () => Promise.resolve("finally!")
);
test(d, "d"); //"finally!"

const e = tryWithFallbacks(
  () => Promise.reject("oops1"),
  () => Promise.reject("oops2"),
  () => Promise.reject("oops3"),
  () => Promise.resolve("penultimate try successful!"),
  () => Promise.reject("this is not reached")
);
test(e, "e"); //"penultimate try successful!"

//a simple function to illustrate the result
function test(promise, name) {
  promise
    .then(result => console.log(`[${name}] completed:`, result))
    .catch(errorResult => console.log(`[${name}] failed:`, errorResult));
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...