承诺然后抓住条款不起作用

时间:2017-03-16 16:17:00

标签: javascript node.js promise es6-promise

背景

我有许多函数可能会或可能不会返回Promise,可能会也可能不会失败,如下例所示:

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}`);
});

因为每个函数可能会也可能不会返回一个可能会或可能不会失败的promise,我从another StackOverflow post修改了executeSafe,它将一个函数及其参数作为参数,然后宣传函数及其结果:

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

我的目标是使用asyncFun函数等待执行一系列Promisified函数,然后返回执行它们的任何函数:

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

问题

asyncFun旨在运行前面描述的众多功能,其中一些我实际上希望看到失败。为了适应这一点,我的asyncFun函数有一个catch子句。此子句仅适用于badFun,不适用于badPromisethen子句永远不会有效。

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);
};

在执行者的调用中放置catch子句甚至不会捕获badFun错误以外的任何其他内容。

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

代码

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!"));

问题:

  • 如何修复我的代码,使其then中的catchasyncFun条款按预期工作?

2 个答案:

答案 0 :(得分:3)

这里的问题是你误解了Promise.all()的工作原理。它的工作方式是,如果所有的承诺成功,它将解析为一个值数组。如果中的任何失败,则会拒绝发生的第一个错误

你似乎期待的是,then中成功的那些被捕获,而失败者被catch捕获,是不可能的。承诺要么解决一次,要么拒绝一次。它不会同时执行这两项操作,并且不会多次执行其中一项操作。 Promise.all()返回单个承诺,以便解决或拒绝。

第三方承诺库确实有“解决”一系列承诺的方法 - 基本上等到他们完成所有事情(成功或失败),然后解析为结果数组。您可以像这样实现它:

// 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));

然后您可以像下面一样使用它。

在一个不相关的说明中,我还修改了您的方法,以便您不需要executeSave的{​​{1}}的修改版本(我认为这是一种令人费解的方式) 。你可以创建使用之前的参数的函数,然后将它们传递给...args

_executeSafe_

附加说明 - 如果您想使用Jared Smith答案中的let goodFun = (fruit) => `I like ${fruit}`; let badFun = (fruit) => { throw `${fruit} is spoiled!`; }; let badPromise = (fruit) => Promise.reject(`Promise failed with ${fruit}`); let goodPromise = (fruit) => Promise.resolve(`Promise succeeded with ${fruit}`); let executeSafe = fun => Promise.resolve().then(fun); 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)); } let asyncFun = (fruit) => allSettled([badFun, goodFun, badPromise, goodPromise] .map(fun => () => fun(fruit)) .map(executeSafe) ); asyncFun("orange").then(results => console.log(results));函数,那么您可以将promisify函数更改为:

asyncFun

答案 1 :(得分:0)

所以这很有效,抓住了。如果传递给promisify的函数返回一个promise,它就会被传递给链。

var fnThatThrows = s => { throw new Error(s) };
var identity = x => x;

var promisify = f => (...args) => new Promise((res, rej) => {
  try {
    res(f(...args));
  } catch (e) {
    rej(e);
  }
});

var rejected = promisify(fnThatThrows);
rejected("foo").then(_ => console.log("shouldn't see")).catch(e => console.log(e)); // logs error

var succeded = promisify(identity);
succeeded("foo").then(val => console.log(val)); // logs 'foo'

如果来自参数函数的promise被拒绝,那么它也会被传递:

var returnsBadPromise = s => Promise.reject(new Error(s));
promisify(returnsBadPromise)("foo").catch(e => console.log(e)); // logs error