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
343 views
in Technique[技术] by (71.8m points)

javascript - Promise then and catch clauses not working

Background

I have a multitude of functions that may or may not return a Promise and that may or may not fail, like in the examples below:

let goodFun = (fruit) => `I like ${fruit}`;

let badFun = (fruit) => {
    throw `${fruit} is spoiled!`;
};

let badPromise = (fruit) => new Promise( (fulfil, reject) => {
    reject(`Promise failed with ${fruit}`);
});

let goodPromise = (fruit) => new Promise((fulfil, reject) => {
    fulfil(`Promise succeeded with ${fruit}`);
});

Because each function may or may not return a promise, which may or may not fail, I modified executeSafe from another StackOverflow post, that takes a function and its arguments as parameters, and then Promisifies the function and its result:

let executeSafe =
    (fun, ...args) => Promise.resolve().then(() => {
        return fun(...args);
    });

My objective with all of this, is to have an asyncFun function that waits for the execution of a batch of functions that were Promisified and then returns whatever came from executing them:

let asyncFun = 
(fruit) => 
    Promise.all([badFun, goodFun, badPromise, goodPromise].map(
        fun => executeSafe(fun, fruit)
    )
);

Problem

asyncFun is designed to run the multitude of functions previously described and some of them I actually expect to see fail. To accommodate for this my asyncFun function has a catch clause. This clause only works with badFun and doesn't work with badPromise. The then clause never works.

let executor = () => {
    let fruitsArr = ["banana", "orange", "apple"];
    let results = [];

    for (let fruit of fruitsArr)
        results.push(
            asyncFun(fruit)
            .then(res => {
                console.log(res);
            })
            .catch(error => {
                console.log(`Error: ${error}`);
            })
        );

    return Promise.all(results);
};

Not even placing the catch clause in the executor's call catches anything else other than the badFun errors.

executor()
    .catch(error => console.log("Failed miserably to catch error!"));

Code

let goodFun = (fruit) => {
  return `I like ${fruit}`;
};

let badFun = (fruit) => {
  throw `${fruit} is spoiled!`;
};

let badPromise = (fruit) => Promise.resolve().then(() => {
  throw `Promise failed with ${fruit}`;
});

let goodPromise = (fruit) => Promise.resolve().then(() => {
  return `Promise succeded with ${fruit}`;
});

let executeSafe =
  (fun, ...args) => Promise.resolve().then(() => {
    return fun(...args);
  });

let asyncFun = (fruit) => Promise.all([badFun, goodFun, badPromise, goodPromise].map(fun => executeSafe(fun, fruit)));

let executor = () => {
  let fruitsArr = ["banana", "orange", "apple"];
  let results = [];

  for (let fruit of fruitsArr)
    results.push(
      asyncFun(fruit)
      .then(res => {
        console.log(res);
      })
      .catch(error => {
        console.log(`Error: ${error}`);
      })
    );

  return Promise.all(results);
};

executor()
  .catch(error => console.log("Failed miserably to catch error!"));
See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

The problem here is that you are misunderstanding how Promise.all() works. The way it works is that if all of the promises succeed, it resolves to an array of values. If any of them fail, it rejects with the first error that occurred.

What you seem to be expecting, that the successful ones are caught in the then and the failed ones are caught in the catch, is not possible. A promise either resolves once or it rejects once. It doesn't do both, and it won't do one or the other multiple times. Promise.all() returns a single promise so it will either resolve or reject.

Third party promise libraries do have methods for "settling" an array of promises - basically waiting until they have all done their thing (succeeded or failed), and resolving to an array of the results. You can implement it like this:

// values is an array of promises and/or non-promise values
function allSettled(values) {
  let settle =
    value => Promise.resolve(value)
    .then(result => ({ state: "fulfilled", value: result }))
    .catch(error => ({ state: "rejected", reason: error }));

  return Promise.all(values.map(settle));
}

// example usage
allSettled(['hello', 'goodbye', Promise.resolve('good'), Promise.reject('bad')])
  .then(results => console.log(results));

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

...