I'm using async
await
in my NodeJs code and the code structure is something as follows.
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. But this code is throwing 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. What am I doing wrong? Should finally
block by synchronous by design? If yes, why so?
Update 1:
If I convert someFunctionThatReturnsRejectedPromise
to good ol' then
and catch
, it works with no problems:
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)
If I await
the returning Promise, problem is solved.
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). Thanks everyone:)
When a Promise rejects, it must be handled before the current call stack clears , or there will be an unhandled rejection. 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. But if there's any delay, the rejected Promise will not be handled immediately; 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.
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
:
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
;
so the function becomes
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. So the control flow went to method main
catch block. Later cleanup
method tries to do the same. Which is to reject the already rejected promise. Thus you get the error
Promise.reject
is a bit different from throw
clause. Please refer throw vs Promise.reject
Which is why removing the await
from cleanup()
or removing the return
from cleanup
method works. Because that will detach the Promise
from the current control flow.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.