Skip to content
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

Promises wiggle their way between nextTick and setImmediate #2736

Closed
ghost opened this issue Sep 8, 2015 · 13 comments
Closed

Promises wiggle their way between nextTick and setImmediate #2736

ghost opened this issue Sep 8, 2015 · 13 comments
Labels
question Issues that look for answers. v8 engine Issues and PRs related to the V8 dependency.

Comments

@ghost
Copy link

ghost commented Sep 8, 2015

This doesn't seem right to me, but feel free to close if it is. Promises somehow always get in between nextTick and setImmediate. This was causing a weird timing bug for me when I switched some code from Bluebird to native promises:

~function(){
  setTimeout            (console.log.bind(console, 'timeout A'));
  process.nextTick      (console.log.bind(console, 'nextTick A'));
  setImmediate          (console.log.bind(console, 'setImmediate A'));
  Promise.resolve().then(console.log.bind(console, 'promise'));
  process.nextTick      (console.log.bind(console, 'nextTick B'));
  setImmediate          (console.log.bind(console, 'setImmediate B'));
  setTimeout            (console.log.bind(console, 'timeout B'));
}();

Native yields:

nextTick A
nextTick B
promise undefined
setImmediate A
setImmediate B
timeout A
timeout B

Bluebird yields:

nextTick A
nextTick B
setImmediate A
promise undefined
setImmediate B
timeout A
timeout B
@rvagg
Copy link
Member

rvagg commented Sep 8, 2015

enter the "micro task queue" .. there was an interesting discussion at the last TSC meeting about possibly using the V8 micro task queue to implement the nextTick (I think), perhaps @trevnorris or @domenic can chime in on this one

@ghost
Copy link
Author

ghost commented Sep 8, 2015

@rvagg -- I'm not certain, but I think this would be related to setImmediate, not nextTick, no? Aren't promises supposed to resolve post-IO listeners?

@vkurchatkin
Copy link
Contributor

This is correct behaviour, bluebird does the wrong thing

@Fishrock123 Fishrock123 added the v8 engine Issues and PRs related to the V8 dependency. label Sep 8, 2015
@ghost
Copy link
Author

ghost commented Sep 8, 2015

@vkurchatkin - well bluebird can't do the correct thing, can it? I don't know of any exposed API to get a callback to fire between nextTick and setImmediate

And given that, I don't understand why some native code to fitting into a "magical" timing spot is correct behavior.

But even if it is, can you clarify when the Promise callbacks run? Is it pre or post IO?

@ChALkeR
Copy link
Member

ChALkeR commented Sep 8, 2015

@zyklus From the docs (for setTimeout):

io.js makes no guarantees about the exact timing of when the callback will fire, nor of the ordering things will fire in

I would not recommend relying on the exact execution order of non-chained events.
If you want to control the execution order — rearrange the callbacks in a way so that the one that you want to be executed later depends on the one that you want to be executed earlier, or implement a queue (that does the same behind the hood).

The order of setImmediate is documented, though:

To schedule the "immediate" execution of callback after I/O events callbacks and before setTimeout and setInterval . Returns an immediateObject for possible use with clearImmediate(). Optionally you can also pass arguments to the callback.

Callbacks for immediates are queued in the order in which they were created. The entire callback queue is processed every event loop iteration. If you queue an immediate from inside an executing callback, that immediate won't fire until the next event loop iteration.

Amd everything looks ok from that side.

@ChALkeR ChALkeR added the question Issues that look for answers. label Sep 8, 2015
@ghost
Copy link
Author

ghost commented Sep 8, 2015

@ChALkeR -- Yes, I realize that and it's not what I'm concerned about. As I said initially, I already changed the code in question.

My question is about whether promises fire before or after IO.

@targos
Copy link
Member

targos commented Sep 8, 2015

IMO there is no "correct" thing, it's just implementation details that can change.
The spec says that all Promise callbacks are handled by the PromiseJobs queue. So you can predict in which order the promise callbacks will be executed inside this queue.
It also says that:

This specification does not define the order in which multiple Job Queues are serviced. An ECMAScript implementation may interweave the FIFO evaluation of the PendingJob records of a Job Queue with the evaluation of the PendingJob records of one or more other Job Queues.

For now, nextTick callbacks are called before promises, but if we change the implementation to use a V8 micro task queue as @rvagg mentioned, it could change completely. It is generally not safe to rely on the execution order of asynchronous callbacks.

@domenic
Copy link
Contributor

domenic commented Sep 8, 2015

There is no correct answer in the ES spec. It is up to the embedding environment. For the web, the intent is for the PromiseJob queue to be subsumed by the microtask queue, pre-IO. Node should choose the same.

@vkurchatkin
Copy link
Contributor

Native promise handlers are executed on a microtask queue which is roughly the same as nextTick, so the run before everything else. Pure javascript implementations should use nextTick for scheduling.

@benjamingr
Copy link
Member

cc @petkaantonov

BTW, I answered your question when you asked it in Stack Overflow with the same answer provided here.

@trevnorris
Copy link
Contributor

@domenic mind elaborating on "the intent is for the PromiseJob queue to be subsumed by the microtask queue, pre-IO"? May just be the jet-lag, but not following.

@domenic
Copy link
Contributor

domenic commented Sep 8, 2015

@trevnorris what ES calls the PromiseJob queue can be implemented by any embedder or engine in any way, as long as they follow the very few restrictions in the ES spec. (Basically, it must be ordered.) The intent is for platforms that have a microtask queue (like HTML, or Node.js, or V8) to use that as the PromiseJob queue, which will fulfill all the requirements, and will also cause promise jobs to happen pre-IO, since that is when microtasks happen.

@trevnorris
Copy link
Contributor

@domenic It's the pre-IO thing that's tripping me up. I can't visualize how that would fit into the event loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Issues that look for answers. v8 engine Issues and PRs related to the V8 dependency.
Projects
None yet
Development

No branches or pull requests

8 participants