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
Deferred: invoke processes in async groups instead of individually #3228
base: main
Are you sure you want to change the base?
Conversation
@jbedard, thanks for your PR! By analyzing the annotation information on this pull request, we identified @gibson042, @mgol and @jaubourg to be potential reviewers |
// Support: Promises/A+ section 2.3.3.3.1 | ||
// https://promisesaplus.com/#point-57 | ||
// Re-resolve promises immediately to dodge false rejection from | ||
// subsequent errors | ||
if ( depth ) { | ||
process(); | ||
deferredTick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this specific line could actually change the execution order of process
s.
Previously this single process
would be invoked before other scheduled async ones. Now it will invoke other scheduled ones first, then this one last, but it is still "immediately" (see comment a couple line above).
Can anyone think of a case where this is wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the only possible callback that might throw and not be caught is the last one, right?
However, would something like deferred.then(fn1).done(fn2)
force fn1 to be synchronous in order to run fn2? If the queue were per-Deferred that wouldn't happen, but it is global/shared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the only possible callback that might throw and not be caught is the last one, right?
I'm not sure actually. Can you think of any examples where a throwing function would be pushed onto processQueue
?
However, would something like deferred.then(fn1).done(fn2) force fn1 to be synchronous in order to run fn2?
All the scenarios I've tried seem to be valid to. Some examples... (where fn1 always executes before fn2, fn3 before fn4 etc).
deferred.then(fn1).done(fn2)
var def = $.Deferred();
def.then(fn1).done(fn2);
def.resolve(-1);
$.when(5).then(fn3);
var def = $.Deferred();
def.then(fn2).done(fn3);
$.when(5).then(fn1);
def.resolve(-1);
var def = $.Deferred();
def.then(fn2).done(fn3);
def.resolve(-1);
$.when(5).done(fn1);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think the case I was looking for was deferred.then(fn1).notify(fn2)
since I think that's the only time special
comes into play? This definitely could use a @gibson042 look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The notify case is an issue, see 4bc074b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR does introduce some sequence jumping. Note how callbacks follow an orderly progression with the current codebase, but the .then
s get clustered and pulled forward by this PR. That's not a problem (and is in fact the whole point), but the other part is potentially a problem: that the mere introduction of a bound Deferred (i.e., constructed by a .then
that returned a thenable) affects all Deferred callbacks, pulling forward to pseudo-synchronicity callbacks that previously waited for the next event loop in front of other callbacks.
I'm against this PR as-is, but in favor of the broader refactoring that was hoped for when the Deferred changes first landed, to abstract out Promises/A+ compatibility code for both Deferred#then
and jQuery.when
, and in particular to eliminate this synchronous call (which exists to catch bad then implementations that throw after invoking a callback) by incrementing maxDepth
earlier in the resolution process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to revert this one line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because then things would probably fall out of spec. I'm instead advocating for broader changes that would eliminate the need for doing something different when depth > 0
, at which point there won't be any queue jumping.
function() { | ||
try { | ||
mightThrow(); | ||
} catch ( e ) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swallowing errors from progress callbacks is a problem. We just went through a big exercise with ready
to prevent its analogous situation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was mainly just to test this out. I'm thinking that exceptions from a progress callback should be handled the same as exceptions in a success/error callback (~20 lines down). If we're ok with that then I think these two methods wrapping mightThrow
can be combined and the special
check will go within that single wrapper. Basically special
just means that mightThrow
throwing does not reject the promise...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So send them through exceptionHook
? Yeah, that makes sense.
Closing & re-opening the PR to trigger the EasyCLA check... |
|
This is a first shot at #3227.
This does not invoke
deferredTick
anywhere else yet.