有没有办法检测被拒绝的承诺是否未处理?

Is there a way detect if a rejected promise is unhandled?

假设我有一个函数 foo,它 returns 是一个承诺。有没有一种方法可以调用该函数,并且只有在拒绝未处理的情况下才可以选择 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
});

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

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

不,没有。当你的函数 returns 是一个承诺时,这会将错误处理留给调用者 - 如果他没有做到这一点,他将得到一个 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)

如您所见,这仅在函数的调用者完全忽略结果时才有用——这种情况很少见。如果他这样做,我建议仍然 .


将使用 handler 作为 onRejected 的默认值:

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

这会在 catchIfUncaught(…).then(res => …); 情况下激活默认处理程序,但对于较长链中的调用者来说可能非常违反直觉。

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

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

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

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