【问题标题】:Promise then and catch clauses not working承诺然后捕获条款不起作用
【发布时间】:2017-08-07 22:41:04
【问题描述】:

背景

我有许多函数可能会或可能不会返回 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}`);
});

因为每个函数可能会或可能不会返回一个可能会或可能不会失败的承诺,所以我从 another StackOverflow post 修改了 executeSafe,它接受一个函数及其参数作为参数,然后 Promisifies 函数及其结果:

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

我对所有这些的目标是拥有一个 asyncFun 函数,该函数等待执行已承诺的一批函数,然后返回执行它们的任何内容:

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

问题:

  • 如何修复我的代码,使其asyncFun 中的thencatch 子句按预期工作?

【问题讨论】:

  • 这真的很令人困惑,我根本不会争辩它是如何完成的。为什么要调用 Promise.resolve().then 而不是使用 Promise 构造函数?您在内部异步调用中 catching 但没有重新抛出,外部 catch 将如何做任何事情?
  • executor catch 理想情况下不会捕获任何东西,因为 asynFun catch 会完成所有工作。但是,它们都不起作用。
  • 我知道它是如何/为什么工作的,但它不寻常的意思是任何习惯使用 Promises 的人都很难阅读你的代码。
  • 我愿意接受建议,如果您认为 Promisify 函数有更好的方法,我会全力以赴!

标签: javascript node.js promise es6-promise


【解决方案1】:

这里的问题是您误解了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));

然后你可以像下面这样使用它。

在不相关的说明中,我还修改了您的方法,以便您不需要采用 ...args 的修改版本的 executeSave(我认为这是一种复杂的方法)。您可以创建使用参数的函数之前将它们传递给_executeSafe_

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

附加说明 - 如果您想使用 Jared Smith 回答中的 promisify 函数,那么您可以将您的 asyncFun 函数更改为:

let asyncFun =
  (fruit) =>
    allSettled([badFun, goodFun, badPromise, goodPromise]
      .map(promisify)
      .map(fun => fun(fruit))
    );

【讨论】:

  • 我犯了这么大的错误,我简直不敢相信......谢谢!
【解决方案2】:

所以这很有效,很好。如果传递给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'

如果来自参数函数的承诺被拒绝,它也会被传递:

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

【讨论】:

  • catch 有多余的。 Promise 构造函数中抛出的错误将被转换为被拒绝的承诺。所以这就足够了:var promisify = f => (...args) => new Promise(res => res(f(...args)));.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-10
  • 2017-04-29
  • 2014-02-11
  • 2021-11-06
  • 2017-11-24
  • 2015-04-15
  • 2019-11-19
相关资源
最近更新 更多