-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unhandled promise tracker called incorrectly #39
Comments
We also had this issue and some discussion about it (and a workaround)... I'm still not sure if it should be considered a bug... more of an nuisance... see HiRoFa/quickjs_es_runtime#74 some googling leads me to believe browsers just update the console error after .catch is actually called (nodejs/help#4286) which we could also implement (put unhandled promise in a separate list and only trigger unhandledTracker in runPendingJobs if no more pending jobs and promise still unhandled?) but even then... async is async.. so you could solve this.. new Promise((res, rej) => {
rej("sync rejection")
}).catch((e) => {
console.log("handled");
}) But not this even if the problem is the same, catch is called later... const prom = new Promise((res, rej) => {
rej("sync rejection")
});
setTimeout(() => {
prom.catch((e) => {
console.log("handled");
})
}, 2000); (e.g. firefox also fixes the first but not the latter...) |
Aha, TIL! I think one of the key things here is the handler is called more than once. First with the promise not being handled (is_handled would be 0) then with 1. Maybe that's ok, since users wouldn't want to do anything in that case? |
how about this: in quickjs.c
Instead of calling the rejection tracker directly we increase the refcount of the promise and enqueue a job which does something like this
that way the is_handled check occurs as a new tick in the eventloop and after .catch is called. and if the promise is unhandled then the tracker is called just once.. Promise fullfillment is unchanged, and the only change is that the rejection call is done in a later tick (and in that case a refcount is upped and downed once extra... and only IF there is a rejectiontracker.. |
The tracker is also called in There is already a |
hmm yeah, but could have same change as above..
if I'm reading the code correctly the promise_reaction_job is enqueued for every promise reaction
|
Hum, looks like it indeed. |
I've started investigating this. The problem is that the results of promises that throw error or is immediately resolved (aka Promise.resolve) does this synchronously rather than doing so in a pending job. So QuickJS doesn't know at that time weather a promise has attached callbacks ( const p = new Promise(() => {
console.log(1)
throw new Error("kaboom");
})
console.log(2) Should report unhandled promise rejection AFTER logging qjs --unhandled-rejection promise.js
1
Possibly unhandled promise rejection: Error: kaboom
at <anonymous> (promise.js:3)
at Promise (native)
at <eval> (promise.js:4)
2 So basically we should:
This is different from the current implementation where we only queue pending job when callbacks are attached: @saghul @bnoordhuis @chqrlie thoughts? |
Calling the reject function synchronously from the promise constructor (but still return the promise object) seems to be per spec; V8 does that, too.1 Deferring the The function you're looking for is 1 Not sure if it's actually observable though. 2 Contrary to what its name suggests, it also handles rejections. |
Not quite sure I follow. When should we not defer? |
Not sure I agree with the initial premise, let's explore! The Promise creation spec: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise-executor It says that if calling the executor is an abrupt termiantion we should call reject(e). Now let's look at how that reject function is created: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-createresolvingfunctions Let's now look at step 7, so the reject function steps: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-createresolvingfunctions Then finally the RejectPromise oepration: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-rejectpromise While V8 seems to enqueue the call to the handler, I don't see any mention to it here. Is my reading correct or did I miss anything? Note that "but everyone does it" is probably a valid answer here! :-) |
Yeah probably. I don't think the spec specifies if it should be enqueue or not. I assume this is an effect of it being impossible to detect if handlers/callbacks are attached otherwise since the constructor always runs first |
See: saghul/txiki.js#310 and bellard/quickjs#112
The text was updated successfully, but these errors were encountered: