如何异步处理未处理的承诺拒绝?

新手上路,请多包涵

我正在努力解决我面临的有关异步/等待和 Promises 的问题。我设法将我的问题归结为以下代码:

 async function sleep(ms: number) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

async function fetchMock(): Promise<any> {
  return new Promise(() => {
    throw 'error fetching result';
  });
}

async function main(): Promise<any> {
  const kickedOffRequest = fetchMock();
  await sleep(10);
  return kickedOffRequest;
}

main()
  .then(() => console.log('resolved promise!'))
  .catch(error => console.error('caught error!', error));

我收到以下警告:

 (node:82245) UnhandledPromiseRejectionWarning: error fetching result
(node:82245) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:82245) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
caught error! error fetching result
(node:82245) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

您可以 在此沙箱 中观察到相同的问题。我注意到注释掉 await sleep(10) 解决了这个问题,但我对承诺的了解显然比我想象的要少。为什么注释该行会使我的程序运行?我很想问如何修复 Promise rejection was handled asynchronously 错误,但我希望一旦我理解了 await sleep(10) 是如何导致我得到的错误的,我将能够自己修复这个错误.

提前感谢您花时间阅读/回答这个问题!

原文由 marhaupe 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

node.js 中未处理拒绝的检测不完善。在被拒绝的 promise 的生命周期中有特定的点,引擎会检查是否有处理程序,并且它并不总是等到最后可能的时刻,所以它可能会错过您添加处理程序的地方。在您的特定情况下,您可能需要在本地附加一个 .catch() 处理程序,然后完成您想要完成的工作,然后重新抛出错误。此解决方法对您有效,同时仍然保持所需的解析/拒绝来自 main() (例如,不将接口更改为 main)。

所以,这不是特别漂亮,但它符合我们在评论中谈到的规范。

  1. main() 调用 fetchMock()
  2. 如果它快速解决或拒绝(在一些自定义延迟时间之前),那么它会推迟解决或拒绝,直到至少从最初调用 fetchMock() 开始的延迟时间已经过去。
  3. 如果 fetchMock() 解决或拒绝的自定义延迟时间更长,则不会添加进一步的延迟。
  4. --- 返回的承诺遵循 fetchMock() main() 返回的承诺,以相同的原因或价值被拒绝或解决。

关键要素是它在调用 fetchMock() 之前捕获时间,然后在 fetchMock() 解决或拒绝时,它决定是否在传递解决/拒绝值之前再延迟时间/通过的原因。

 function sleep(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

function fetchMock() {
    return new Promise((resolve) => {
        throw 'error fetching result';
        //resolve('this is our result');
    });
}

function handler(start, minWaitTime, isErr = false) {
    return async function(val) {
        let diff = minWaitTime - (Date.now() - start);
        if (diff > 0) {
            await sleep(diff);
        }
        if (isErr) {
            throw val;
        } else {
            return val;
        }
    }
}

function main() {
    let start = Date.now();
    const minWaitTime = 1000;
    return fetchMock().then(handler(start, minWaitTime), handler(start, minWaitTime, true));
}

main()
    .then(() => console.log('resolved promise!'))
    .catch(error => console.error('caught error!', error));

Note, also that sleep() and fetchMock() already directly return promises and don’t use await so there is no requirement for them to be async

原文由 jfriend00 发布,翻译遵循 CC BY-SA 4.0 许可协议

promises 的最初概念是,在将 catch 处理程序附加到它之前,您可以让一个被拒绝的 promise 搁置一段时间。例如,Firefox 仅在从内存中垃圾回收没有拒绝处理程序的被拒绝承诺时才会警告未捕获的拒绝错误。

有人认为程序员无法正确管理 promise 拒绝并更改 HTML 规范以要求浏览器在代码返回到事件循环之前没有添加拒绝处理程序的情况下抛出“未处理的 promise 拒绝”错误。

(我认为在控制返回到事件循环之前,未处理的拒绝可以在微任务队列中无错误地存在一两个滴答,但最近没有测试过。)

ECMAScript 规范添加了一种抽象方法来 通知主机环境未处理的承诺拒绝,但没有指定应该采取什么行动(如果有的话)。

根据具体情况,您可以通过添加从未使用过的拒绝处理程序来防止通知主机。原因是向 promise 添加一个虚拟拒绝处理程序意味着如果它被拒绝它已经有一个拒绝处理程序 - 或者如果它被拒绝主机被通知承诺现在有一个拒绝处理程序 - 你可以调用 thencatch 同一个承诺多次。

改变

async function fetchMock(){
  return new Promise(() => {
    throw 'error fetching result';
  });
}

async function fetchMock(){
  let promise = new Promise(() => {
    throw 'error fetching result';
  });
  promise.catch(()=>null); // unused rejection handler
  return promise;
}

应该解决在 V8 中实现的不需要的 HTML5 宿主行为,V8 是 node.js 中使用的 JavaScript 引擎。

原文由 traktor 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题