简体   繁体   English

在异步 function 的 finally 块中等待导致 PromiseRejectionHandledWarning

[英]await in finally block of async function causes PromiseRejectionHandledWarning

I'm using async await in my NodeJs code and the code structure is something as follows.我在我的 NodeJs 代码中使用async await ,代码结构如下。

async function main(){
    try {
        await someFunctionThatReturnsRejectedPromise()
    } catch(e) {
        console.log(e)
    }
}

async function someFunctionThatReturnsRejectedPromise() {
    try {
        await new Promise((resolve,reject) => {
            setTimeout(() => {
                reject('something went wrong')
            }, 1000);
        })
    } catch(e) {
        return Promise.reject(e)
    } finally {
        await cleanup() // remove await here and everything is fine
    }
}


function cleanup() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('cleaup successful')
        }, 1000);
    })
}

main();

In the finally block, I'm doing some async cleanup that will surely resolve.在 finally 块中,我正在做一些肯定会解决的async清理。 But this code is throwing PromiseRejectionHandledWarning但是这段代码正在抛出PromiseRejectionHandledWarning

(node:5710) UnhandledPromiseRejectionWarning: something went wrong
(node:5710) 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:5710) [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.
something went wrong
(node:5710) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

As far as I understand, I'm not leaving any promise unhandled here.据我了解,我不会在这里留下任何未处理的 promise。 What am I doing wrong?我究竟做错了什么? Should finally block by synchronous by design? finally应该按设计同步阻止吗? If yes, why so?如果是,为什么会这样?

Update 1:更新 1:

If I convert someFunctionThatReturnsRejectedPromise to good ol' then and catch , it works with no problems:如果我将someFunctionThatReturnsRejectedPromise转换为 good ol' thencatch ,它可以正常工作:

function someFunctionThatReturnsRejectedPromise() {
    return (new Promise((resolve,reject) => {
        setTimeout(() => {
            reject('something went wrong')
        }, 1000);
    })).catch(e => {
        return Promise.reject(e)
    }).finally(() => {
        return cleanup()
    })
}

Update 2: (Understood the problem)更新2:(理解问题)

If I await the returning Promise, problem is solved.如果我await返回的 Promise,问题就解决了。

 return await Promise.reject(e)

And this makes me understand what I was doing wrong.这让我明白我做错了什么。 I was breaking the await chain (partially synonymous to not returning a Promise in then / catch syntax).我打破了await链(部分与在then / catch语法中不返回Promise同义)。 Thanks everyone:)感谢大家:)

When a Promise rejects, it must be handled before the current call stack clears , or there will be an unhandled rejection.当一个 Promise 拒绝时,必须在当前调用堆栈清除之前对其进行处理,否则将出现未处理的拒绝。 You have:你有:

} catch (e) {
  return Promise.reject(e)
} finally {
  await cleanup() // remove await here and everything is fine
}

If you remove await , the someFunctionThatReturnsRejectedPromise returns immediately after the rejected Promise is constructed, so the Promise.reject(e) , the rejected Promise, is caught by the catch in main right after.如果删除awaitsomeFunctionThatReturnsRejectedPromise在构造了被拒绝的 Promise 后立即返回,因此Promise.reject(e) ,被拒绝的 ZA5A3F0F287A448982AAC520CFFE4 在main之后被catch in 捕获。 But if there's any delay, the rejected Promise will not be handled immediately;但是如果有任何延迟,被拒绝的 Promise 将不会立即处理; your await cleanup() will mean that the rejected Promise goes unhandled for a period of time, before someFunctionThatReturnsRejectedPromise returns, which means that main 's catch can't handle the rejected Promise in time.您的await cleanup()将意味着被拒绝的 Promise 在someFunctionThatReturnsRejectedPromise返回之前有一段时间未处理,这意味着maincatch无法及时处理被拒绝的 Promise 。

Another method you could use would be to wrap the error in an Error instead of a Promise.reject , and then check if the result is an instanceof Error in main :您可以使用的另一种方法是将错误包装在Error中而不是Promise.reject中,然后检查结果是否是main中的instanceof Error

 window.addEventListener('unhandledrejection', () => console.log('unhandled rejection;')); async function main() { const result = await someFunctionThatReturnsRejectedPromise(). if (result instanceof Error) { console:log('Error "caught" in main,'. result;message), } } async function someFunctionThatReturnsRejectedPromise() { try { await new Promise((resolve, reject) => { setTimeout(() => { reject('something went wrong') }; 1000); }) } catch (e) { return new Error(e); } finally { await cleanup() } } function cleanup() { return new Promise(resolve => { setTimeout(() => { resolve('cleaup successful') }); }) } main();

Updated answer replace the更新的答案替换

Promise.reject(e) with throw e ; Promise.reject(e)throw e ;

so the function becomes所以 function 变成

async function someFunctionThatReturnsRejectedPromise() {
   try {
       await new Promise((resolve,reject) => {
           setTimeout(() => {
               reject('something went wrong')
           }, 1000);
       })
   } catch(e) {
       throw e;
   } finally {
       await cleanup() // remove await here and everything is fine
   }
}

Reason原因

someFunctionThatReturnsRejectedPromise method rejects the Promise first. someFunctionThatReturnsRejectedPromise方法首先拒绝Promise So the control flow went to method main catch block.所以控制流转到方法main catch 块。 Later cleanup method tries to do the same.后来的cleanup方法尝试做同样的事情。 Which is to reject the already rejected promise.也就是拒绝已经被拒绝的promise。 Thus you get the error因此你得到错误

Promise.reject is a bit different from throw clause. Promise.rejectthrow子句有点不同。 Please refer throw vs Promise.reject请参考throw vs Promise.reject

Which is why removing the await from cleanup() or removing the return from cleanup method works.这就是为什么从cleanup()中删除await或从cleanup方法中删除return有效的原因。 Because that will detach the Promise from the current control flow.因为这会将Promise从当前控制流中分离出来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM