【问题标题】:Is there a way detect if a rejected promise is unhandled?有没有办法检测被拒绝的承诺是否未处理?
【发布时间】:2020-01-07 11:51:36
【问题描述】:

假设我有一个函数foo,它返回一个承诺。有没有办法调用该函数,并且只有在拒绝未处理的情况下才可以选择Promise.prototype.catch 结果?我想要一个适用于 node.js 和浏览器的解决方案。例如:

const fooResult = foo();
// pass fooResult somewhere else where fooResult may be caught with catch
catchIfUncaught(fooResult, (err) => {
  console.log(err); // should be foo rejection only if the rejection is not caught elsewhere
  // no unhandled rejection occurs
});

【问题讨论】:

  • 请问跨环境是什么意思?如果您已经确定了您的代码无法运行的特定环境,那么具体说明它是什么以及该环境中出了什么问题是有意义的
  • 我的意思是在 node.js 和浏览器中。我知道在这两种环境中都有全局未处理的拒绝事件,并且希望有一个在这两种环境中都有效的解决方案。
  • 我更新了示例,以便将fooResult 传递到其他地方。我希望 catchIfUncaught 处理程序仅在 fooResult 从未被捕获时运行。
  • 如果一个catch块没有返回一个被拒绝的promise(也没有抛出一个错误),后续的catch块将不会被调用。
  • @sp00m 我们如何确定catch 调用的顺序?它是基于执行顺序吗?您还可以指出规范中说明情况的部分吗?

标签: javascript node.js promise es6-promise


【解决方案1】:

不,没有。当您的函数返回一个承诺时,将错误处理留给调用者 - 如果他没有这样做,他将收到一个 unhandledpromiserejection 事件。


我能想象的唯一破解方法是识别 then 调用,然后取消您自己的错误处理:

function catchIfUncaught(promise, handler) {
    let handled = false;
    promise.catch(err => {
        if (!handled)
             handler(err);
    });
    promise.then = function(onFulfilled, onRejected) {
        handled = true;
        return Promise.prototype.then.call(this, onFulfilled, onRejected);
    };
    return promise;
}

例子:

catchIfUncaught(Promise.reject(), err => console.log("default handler", err));

catchIfUncaught(Promise.reject(), err => console.log("default handler", err))
.catch(err => console.log("catch handler", err));

catchIfUncaught(Promise.reject(), err => console.log("default handler", err))
.then(null, err => console.log("then rejection handler", err));

catchIfUncaught(Promise.reject(), err => console.log("default handler", err))
.then(res => {})
.catch(err => console.log("chained catch handler", err));

catchIfUncaught(Promise.reject(), err => console.log("default handler", err))
.then(res => {});
// unhandled rejection (on the chained promise)

如您所见,这仅在您的函数调用者完全忽略结果时才有用 - 这确实不常见。如果他这样做,我建议仍然let the caller handle errors


A similar hack I devised earlier 将使用handler 作为onRejected 的默认值:

…
promise.then = function(onFulfilled, onRejected = handler) {
//                                              ^^^^^^^^^
    return Promise.prototype.then.call(this, onFulfilled, onRejected);
};

这将激活 catchIfUncaught(…).then(res => …); 案例中的默认处理程序,但可能与较长链中的调用者高度反直觉。

另外请注意,这两个 hack 都不能与 await 一起正常工作,它们总是会导致调用者需要捕获的异常。对于任何其他需要 thenable 的内置函数也是如此——它们总是使用两个参数调用 .then

【讨论】:

  • 我想你可能误解了这个前提。我不是在问 catchIfUncaught 是否被捕获,而是 Promise 传递给了 catchIfUncaught。假设将 fooResult 分配给外部范围内的类或变量的属性。在我的脑海中,catchIfUncaught 返回 void。本质上,我有一个承诺,我想听听它未经处理的拒绝。可能不太可能,但感谢您的回复。
  • @brainkim 传递给catchIfUncaught 的promise 与它返回的promise 相同,以便通过链接更容易使用。与fooResult = Promise.reject(); catchIfUncaught(fooResult, …); fooResult.then(…).… 相同。
  • 谢谢,我认为这样的解决方案最终将适用于我的用例。
  • 作为更新,我发现这个 hack 适用于 node.js v10,但从 v12 开始不起作用。问题是 await 不会触发本机 Promise 的实际方法,因此重新分配它们没有任何作用。解决方案是创建一个普通的旧 javascript 对象,该对象具有本机承诺的所有方法,覆盖 then 和 catch 方法,但不让该对象成为本机承诺的实例。如果有机会,我会更新示例来演示这一点。
  • @brainkim 啊,they changed this,因为 ES10 现在使用通过 PromiseResolve 的快捷方式。然而,正如我在最后几段中所说,这些黑客从来没有与await 一起工作过。也许您应该改为查看async hooks
【解决方案2】:

你可以看看这个包https://npmjs.org/package/e-promises

但您必须更改代码才能使用新机制

  1. 导入EPromise
  2. 使用YourPromise extends EPromise 扩展它(可选)
  3. YourPromise.prototype.unchaught 分配给您的catchIfUncaught 实现
  4. foo更改代码,每个做出承诺的地方都必须更改为使用YourPromisenew YourPromise(executor) / YourPromise.resolve / YourPromise.all / ...

【讨论】:

    【解决方案3】:

    如果你不关心传递的情况,你可以只捕获错误情况。

    catchIfUncaught.catch(function (err) {
      console.error('We had an error: ', err)
    })
    

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-30
      • 2020-02-08
      相关资源
      最近更新 更多