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

时间:2019-11-26 22:47:26

标签: javascript error-handling promise async-await

我正在努力解决有关异步/等待和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)

您可以观察到相同的问题in this sandbox。我注意到注释掉await sleep(10)可以解决此问题,但显然我对诺言的了解比我想象的要少。为什么注释该行使我的程序正常工作?我很想问如何解决Promise rejection was handled asynchronously错误,但是我希望一旦我了解了await sleep(10)导致错误的原因,我将能够自行解决此错误。

在此先感谢您抽出时间阅读/回答此问题!

3 个答案:

答案 0 :(得分:2)

promise的原始概念是,在将捕获处理程序附加到该promise之前,您可以将被拒绝的promise闲置一段时间。例如,Firefox仅在从内存中垃圾收集没有拒绝处理程序的被拒绝的诺言时,才警告未捕获的拒绝错误。

有人认为不能通过正确管理承诺拒绝来信任程序员,并更改了HTML规范,要求浏览器抛出“未处理的承诺拒绝”错误,前提是被拒绝的承诺没有在代码返回事件循环之前添加拒绝处理程序。

(我认为,未处理的拒绝可以在微任务队列中保留一两秒钟而不会出错,直到控制权正确返回事件循环,但最近没有对其进行测试。)

ECMAScript规范添加了notifying the host environment of an unhandled promise rejection的抽象方法,但未指定应采取的措施。

根据情况,可以通过添加从未使用过的拒绝处理程序来防止通知主机。原因是,向承诺中添加虚拟拒绝处理程序意味着应该将其拒绝,它已经有一个拒绝处理程序;或者,如果被拒绝,则主机会收到通知,承诺现在有了一个拒绝处理程序,您可以调用{{1} }和then多次出现在同一承诺上。

更改

catch

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

应该解决在V8(节点中使用的JavaScript引擎)中实现的有害HTML5主机行为。

答案 1 :(得分:1)

问题是fetchMock立即拒绝 ,并且当Promise拒绝时,拒绝它的时候,它必须与{ {1}},以防止未处理的承诺拒绝。

.catch函数仍在等待await sleep(10)解析的情况下,kickedOffRequest会拒绝main承诺。遇到拒绝时,解释器不会向前看来查看将来是否可能会遇到Promise(例如,查看Promise是否被退回或被捕获)-必须捕获Promise 现在

当您移除sleep时,await仍然会成为被拒绝的Promise,但它是从kickedOffRequest立即返回的。 ,因此Promise拒绝了,它可以被外部main看到并捕获,因此没有未处理的拒绝警告。


如果Promise拒绝可以在拒绝时得到正确处理,这是很好的做法,但是如果这不是您的选择,则可以在内部Promise的末尾放置另一个.catch。这样,警告就不会出现(因为.catch的存在,即使.catch并没有做任何有意义的事情),您可以稍后查看是否确实发生错误:

.catch

这与async function sleep(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } async function fetchMock(){ return new Promise(() => { throw 'error fetching result'; }); } async function main() { const kickedOffRequest = fetchMock() .then(resolved => ({ resolved })) .catch(rejected => ({ rejected })); await sleep(10); return kickedOffRequest; } main() .then(({ resolved, rejected }) => { if (resolved) { console.log('resolved promise!'); } else { console.error('caught error!', rejected); } });的工作原理非常相似(尽管它更适用于需要解析的Promise数组 array ):

Promise.allSettled

答案 2 :(得分:1)

在node.js中检测未处理的拒绝是不完善的。拒绝诺言的生命周期中有一些特定的地方,引擎会检查那里是否有处理程序,并且它并不总是等到最后一刻才出现,因此它可能会错过您添加处理程序的地方。在您的特定情况下,您可能需要在本地附加一个.catch()处理程序,然后完成要执行的工作,然后重新抛出该错误。此解决方法将为您工作,同时仍保持main()所需的解析/拒绝(例如,无需将界面更改为main)。

因此,这并不是特别漂亮,但符合我们在评论中讨论的规范。

  1. main()呼叫fetchMock()
  2. 如果它快速解决或拒绝(在某些自定义延迟时间之前),则它会推迟解决或拒绝,直到至少从最初调用fetchMock()开始经过了延迟时间为止。
  3. 如果fetchMock()花费比自定义延迟时间更长的时间来解决或拒绝,则不再添加任何延迟。
  4. 然后main()返回的保证就跟随fetchMock()返回的保证被拒绝或以相同的原因或价值解决。

关键要素是它会在调用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));

请注意,sleep()fetchMock()已经直接返回诺言,并且不使用await,因此不要求它们为async