You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
unhandledRejection falls into infinite recursion #17913
Version: 8+ (probably all versions that supports unhandledRejection event)
Platform: linux
Subsystem: process
According to documentation of uncaughtException - 'Exceptions thrown from within the event handler will not be caught. Instead the process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion.'.
So why unhandledRejection allows you to fall into infinite recursion if code within handler creates unhandled promise rejection ? I expect the same behavior as for uncaughtException (exit process with non-zero code).
unhandledRejection handler may contain some logic to log that situation, and owing to the fact that 3-d party logger implementation can produce unhandled rejection within himself , we can have infinite recursion.
I propose to emit warning about promise unhandled rejection on the second recursion call, and exit process with error code, probably some special code to indicate specificity of this exit event.
I don't expect that any code rely on this behavior, so I assume that it's safe to fix it.
May prepare Christmas fix for that.
Working as intended, IMO. Promise.reject(3) doesn't throw an exception, it creates a promise that triggers a new unhandledRejection event on the next tick. There is no recursion, just repeated unhandledRejection events.
If you replace it with a throw 'boom', you get the behavior you expect. Replace it with Promise.reject(3).catch(console.error) and you get only a single unhandledRejection event.
By saying infinite recursion I mean that we infinitely entering that function. And there is a case when no another code would be run cuz user's micro function can completely fill up promise micro stack.
As a plus sign, I don't see any advantage in current implementation.
If after unhandledRejection callback execution you have one another unhandledRejection there is a chance (a big change) that you will have it infinitely. And as a result side effect (infinite functions call, whatever you call it).
@apapirovski but you also don't like the idea about process.exit on the second sequential call ?
don't get me wrong, its okay to emit unhandledRejection several times during program execution, but IMO it's not okay to emit this event after processing callback for that event..
@eduardbcom I think that we should conform to the expected async behaviour of promises. The problem is that the current code that emits unhandledRejection inadvertently creates an infinite loop — but it probably shouldn't.
@bnoordhuis I think we need to at least allow the nextTick loop to proceed but I could see an argument for even allowing the nextTick loop to finish and only invoke any new promise rejection on the next run of _tickCallback. Thoughts? This is the problematic code:
I think the expected behavior is for them to be fatal. However, it is very hard to reach an agreement on the matter. I have a polyfill for that behavior if you want to try that out: https://github.com/mcollina/make-promises-safe.
I think this is too much of an edge case and a programmer error: I do not think it's Node.js responsibility to handle.
Activity
[-]unhandledRejection fall into infinite recursion[/-][+]unhandledRejection falls into infinite recursion[/+]eduardbme commentedon Dec 29, 2017
I propose to emit warning about promise unhandled rejection on the second recursion call, and exit process with error code, probably some special code to indicate specificity of this exit event.
I don't expect that any code rely on this behavior, so I assume that it's safe to fix it.
May prepare Christmas fix for that.
bnoordhuis commentedon Dec 29, 2017
Working as intended, IMO.
Promise.reject(3)doesn't throw an exception, it creates a promise that triggers a newunhandledRejectionevent on the next tick. There is no recursion, just repeatedunhandledRejectionevents.If you replace it with a
throw 'boom', you get the behavior you expect. Replace it withPromise.reject(3).catch(console.error)and you get only a singleunhandledRejectionevent.eduardbme commentedon Dec 29, 2017
Okay, let me show you some another code.
you got the idea ?
eduardbme commentedon Dec 29, 2017
Why we cannot say the same thing about uncaughtException. "There is no recursion, just repeated uncaughtException events." ?
But uncaughtException IMO has correct behavior avoiding infinite recursion. The same should have unhandledRejection.
apapirovski commentedon Dec 29, 2017
@eduardbcom Since
unhandledRejectionhappens onnextTick, there's no actual infinite loop. The other code in your app can still run.eduardbme commentedon Dec 29, 2017
@apapirovski
By saying infinite recursion I mean that we infinitely entering that function. And there is a case when no another code would be run cuz user's micro function can completely fill up promise micro stack.
eduardbme commentedon Dec 29, 2017
As a plus sign, I don't see any advantage in current implementation.
If after
unhandledRejectioncallback execution you have one anotherunhandledRejectionthere is a chance (a big change) that you will have it infinitely. And as a result side effect (infinite functions call, whatever you call it).Any reason to love this behavior ?
@apapirovski that was not working code :)
apapirovski commentedon Dec 29, 2017
@eduardbcom After looking at the code, I don't think the current behaviour is intended. Pretty sure it was meant to let the event loop proceed first.
eduardbme commentedon Dec 29, 2017
@apapirovski but you also don't like the idea about process.exit on the second sequential call ?
don't get me wrong, its okay to emit
unhandledRejectionseveral times during program execution, but IMO it's not okay to emit this event after processing callback for that event..apapirovski commentedon Dec 29, 2017
@eduardbcom I think that we should conform to the expected async behaviour of promises. The problem is that the current code that emits
unhandledRejectioninadvertently creates an infinite loop — but it probably shouldn't.@bnoordhuis I think we need to at least allow the
nextTickloop to proceed but I could see an argument for even allowing thenextTickloop to finish and only invoke any new promise rejection on the next run of_tickCallback. Thoughts? This is the problematic code:node/lib/internal/process/promises.js
Lines 100 to 116 in 4117e22
As you can see, the
pendingUnhandledRejectionscan just get refilled with new rejections while we're iterating over it. That seems problematic to me.eduardbme commentedon Dec 29, 2017
Can fix it, but we need to reach an agreement of the expected behavior.
bnoordhuis commentedon Dec 29, 2017
Something like this is acceptable, IMO:
(And might as well change to that a
forloop for efficiency:)Anything more complex is probably not worth it because it's an edge case and the plan is to make unhandled rejections fatal errors anyway.
eduardbme commentedon Dec 31, 2017
Even in the case we listen that event ?
mcollina commentedon Jan 3, 2018
I think the expected behavior is for them to be fatal. However, it is very hard to reach an agreement on the matter. I have a polyfill for that behavior if you want to try that out: https://github.com/mcollina/make-promises-safe.
I think this is too much of an edge case and a programmer error: I do not think it's Node.js responsibility to handle.
promises: refactor rejection handling
promises: refactor rejection handling
promises: refactor rejection handling