-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Conversation
Your approach looks okay to me but I'm curious what made you decide to go with this instead of reimplementing process.nextTick() in terms of V8::EnqueueMicrotask()? It is because V8::RunMicrotasks() breaks when a callback throws an exception? That has been fixed in upstream V8 although in a way that is slightly incompatible with process.nextTick(): V8::RunMicrotasks() drops pending microtasks whereas process.nextTick() continues on the next tick (which isn't particularly sound IMO, but that aside.) |
@bnoordhuis yeah, mostly. I'm afraid of other possible subtle incompatibilities between |
nextTickQueue.push({ | ||
callback: runMicrotasksCallback, | ||
domain: null, | ||
_asyncQueue: undefined |
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.
You can drop _asyncQueue
. It'll be removed soon anyway.
Well, did a quick performance test and the times are comparable between passing a function to |
@trevnorris switching to
@shigeki your PR doesn't fix this issue, only one particular case. |
@vkurchatkin Yeah, I realize that it's currently not possible. Hence my "If the implementation could be done safely." :-) |
@vkurchatkin Please apply the following diff. I like to prevent pollution of the diff --git a/src/node.cc b/src/node.cc
index c1f6d20..2223c36 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -978,6 +978,7 @@ void SetupNextTick(const FunctionCallbackInfo<Value>& args) {
assert(args[0]->IsObject());
assert(args[1]->IsFunction());
+ assert(args[2]->IsObject());
// Values use to cross communicate with processNextTick.
Local<Object> tick_info_obj = args[0].As<Object>();
@@ -988,6 +989,8 @@ void SetupNextTick(const FunctionCallbackInfo<Value>& args) {
env->set_tick_callback_function(args[1].As<Function>());
+ NODE_SET_METHOD(args[2].As<Object>(), "runMicrotasks", RunMicrotasks);
+
// Do a little housekeeping.
env->process_object()->Delete(
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick"));
@@ -2804,8 +2807,6 @@ void SetupProcessObject(Environment* env,
NODE_SET_METHOD(process, "_setupNextTick", SetupNextTick);
NODE_SET_METHOD(process, "_setupDomainUse", SetupDomainUse);
- NODE_SET_METHOD(process, "_runMicrotasks", RunMicrotasks);
-
// pre-set _events object for faster emit checks
process->Set(env->events_string(), Object::New(env->isolate()));
}
diff --git a/src/node.js b/src/node.js
index 064a005..e7b45ad 100644
--- a/src/node.js
+++ b/src/node.js
@@ -294,10 +294,10 @@
var _runAsyncQueue = tracing._runAsyncQueue;
var _loadAsyncQueue = tracing._loadAsyncQueue;
var _unloadAsyncQueue = tracing._unloadAsyncQueue;
- var _runMicrotasks = process._runMicrotasks;
var microtasksScheduled = false;
- delete process._runMicrotasks;
+ // Used to run V8's micro task queue.
+ var _runMicrotasks = {};
// This tickInfo thing is used so that the C++ code in src/node.cc
// can have easy accesss to our nextTick state, and avoid unnecessary
@@ -316,7 +316,9 @@
process._tickCallback = _tickCallback;
process._tickDomainCallback = _tickDomainCallback;
- process._setupNextTick(tickInfo, _tickCallback);
+ process._setupNextTick(tickInfo, _tickCallback, _runMicrotasks);
+
+ _runMicrotasks = _runMicrotasks.runMicrotasks;
function tickDone() {
if (tickInfo[kLength] !== 0) { Then ping me back. Should be ready to roll. |
@trevnorris all set |
Thanks. Merged into 30bd7b6. |
\o/ very excited. A new 0.11 release would be much appreciated if it's not too much trouble. (Which it might be.) |
🍻 Been waiting on this haha. |
Previously we enqueue Promise callbacks with "process.nextTick". This turns out incompatible with node/v8 and causing execution order differences, breaking some Promise uses. This change implements a micro task queue in each ContextShim and uses it to run Promise callbacks (read: nodejs/node-v0.x-archive#8325).
Previously we enqueue Promise callbacks with "process.nextTick". This turns out incompatible with node/v8 and causing execution order differences, breaking some Promise uses. This change implements a micro task queue in each ContextShim and uses it to run Promise callbacks (read: nodejs/node-v0.x-archive#8325). PR-URL: nodejs#45 Reviewed-By: Sandeep Agarwal <Agarwal.Sandeep@microsoft.com>
Since v8 introduced
Promise
support it became possible to schedule tick callback which will be executed only after next javascript invocation:It may look like this issue affects only first tick, but it's not:
This happens because v8 uses microtask queue to run promise callbacks (and also
Object.observe
callbacks), which is essentially the same as nextTick queue. Together they don't play really nice: nextTick callback may schedule a microtask and vice versa. If queues are flushed in any particular order some callbacks won't be called. To handle this problem we need to flush queues in a loop until both are empty.This PR implements the following algorithm:
This attempts to make two microtasks queues behave as one. Unfortunately, microtask queue has higher priority and can potentially prevent nextTick callbacks added before from running:
It looks like there is nothing we can do about it without direct control over microtask queue. For same reason there is no way to make microtask queue work with domains.
fixes #7714
/cc @tjfontaine @trevnorris @indutny