Skip to content

Commit 4baa4df

Browse files
committed
move away from process.nextTick
There are two problems with process.nextTick. Less severe is that it reduces performance for all async callback from native code. The more severe one is that it causes weird and unpredictable behavior when trying to interop with promises and async/await code. In particular, we have an invariant where we always emit certain events and invoke certain callbacks "asynchronously". However, that currently doesn't apply to Promise, since we "force" asynchronousity throug process.nextTick which occurs before any microtick. Hence, for any promise/micro-tick based code things actually appear to occur synchronously. Refs: #51070
1 parent bb2dd0e commit 4baa4df

38 files changed

+126
-126
lines changed

lib/_http_client.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ function ClientRequest(input, options, cb) {
342342
if (typeof optsWithoutSignal.createConnection === 'function') {
343343
const oncreate = once((err, socket) => {
344344
if (err) {
345-
process.nextTick(() => this.emit('error', err));
345+
queueMicrotask(() => this.emit('error', err));
346346
} else {
347347
this.onSocket(socket);
348348
}
@@ -405,7 +405,7 @@ ClientRequest.prototype.abort = function abort() {
405405
return;
406406
}
407407
this.aborted = true;
408-
process.nextTick(emitAbortNT, this);
408+
queueMicrotask(() => emitAbortNT(this));
409409
this.destroy();
410410
};
411411

@@ -722,11 +722,11 @@ function responseKeepAlive(req) {
722722
// has no 'error' handler.
723723

724724
// There are cases where _handle === null. Avoid those. Passing undefined to
725-
// nextTick() will call getDefaultTriggerAsyncId() to retrieve the id.
725+
// queueMicrotask will call getDefaultTriggerAsyncId() to retrieve the id.
726726
const asyncId = socket._handle ? socket._handle.getAsyncId() : undefined;
727727
// Mark this socket as available, AFTER user-added end
728728
// handlers have a chance to run.
729-
defaultTriggerAsyncIdScope(asyncId, process.nextTick, emitFreeNT, req);
729+
defaultTriggerAsyncIdScope(asyncId, queueMicrotask, emitFreeNT, req);
730730

731731
req.destroyed = true;
732732
if (req.res) {
@@ -860,7 +860,7 @@ function listenSocketTimeout(req) {
860860
ClientRequest.prototype.onSocket = function onSocket(socket, err) {
861861
// TODO(ronag): Between here and onSocketNT the socket
862862
// has no 'error' handler.
863-
process.nextTick(onSocketNT, this, socket, err);
863+
queueMicrotask(() => onSocketNT(this, socket, err));
864864
};
865865

866866
function onSocketNT(req, socket, err) {

lib/_http_incoming.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,10 @@ IncomingMessage.prototype._destroy = function _destroy(err, cb) {
236236
e = null;
237237
}
238238
cleanup();
239-
process.nextTick(onError, this, e || err, cb);
239+
queueMicrotask(() => (onError, this, e || err, cb));
240240
});
241241
} else {
242-
process.nextTick(onError, this, err, cb);
242+
queueMicrotask(() => onError(this, err, cb));
243243
}
244244
};
245245

lib/_http_outgoing.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
888888
function onError(msg, err, callback) {
889889
const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined;
890890
defaultTriggerAsyncIdScope(triggerAsyncId,
891-
process.nextTick,
891+
queueMicrotask,
892892
emitErrorNt,
893893
msg,
894894
err,
@@ -935,7 +935,7 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
935935
if (!msg.destroyed) {
936936
onError(msg, err, callback);
937937
} else {
938-
process.nextTick(callback, err);
938+
queueMicrotask(() => callback(err));
939939
}
940940
return false;
941941
}
@@ -969,14 +969,14 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
969969
} else {
970970
debug('This type of response MUST NOT have a body. ' +
971971
'Ignoring write() calls.');
972-
process.nextTick(callback);
972+
queueMicrotask(callback);
973973
return true;
974974
}
975975
}
976976

977977
if (!fromEnd && msg.socket && !msg.socket.writableCorked) {
978978
msg.socket.cork();
979-
process.nextTick(connectionCorkNT, msg.socket);
979+
queueMicrotask(() => connectionCorkNT(msg.socket));
980980
}
981981

982982
let ret;
@@ -1110,7 +1110,7 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
11101110
} else if (!this._headerSent || this.writableLength || chunk) {
11111111
this._send('', 'latin1', finish);
11121112
} else {
1113-
process.nextTick(finish);
1113+
queueMicrotask(finish);
11141114
}
11151115

11161116
if (this[kSocket]) {

lib/_http_server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ function resOnFinish(req, res, socket, state, server) {
994994

995995
res.detachSocket(socket);
996996
clearIncoming(req);
997-
process.nextTick(emitCloseNT, res);
997+
queueMicrotask(() => emitCloseNT(res));
998998

999999
if (res._last) {
10001000
if (typeof socket.destroySoon === 'function') {

lib/_tls_wrap.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ function TLSSocket(socket, opts) {
609609
}
610610

611611
// Read on next tick so the caller has a chance to setup listeners
612-
process.nextTick(initRead, this, socket);
612+
queueMicrotask(() => initRead(this, socket));
613613
}
614614
ObjectSetPrototypeOf(TLSSocket.prototype, net.Socket.prototype);
615615
ObjectSetPrototypeOf(TLSSocket, net.Socket);
@@ -999,7 +999,7 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
999999
this._handle.renegotiate();
10001000
} catch (err) {
10011001
if (callback) {
1002-
process.nextTick(callback, err);
1002+
queueMicrotask(() => callback(err));
10031003
}
10041004
return false;
10051005
}

lib/child_process.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ function spawn(file, args, options) {
783783
if (options.signal) {
784784
const signal = options.signal;
785785
if (signal.aborted) {
786-
process.nextTick(onAbortListener);
786+
queueMicrotask(onAbortListener);
787787
} else {
788788
addAbortListener ??= require('events').addAbortListener;
789789
const disposable = addAbortListener(signal, onAbortListener);

lib/dgram.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ function doConnect(ex, self, ip, address, port, callback) {
435435

436436
if (ex) {
437437
state.connectState = CONNECT_STATE_DISCONNECTED;
438-
return process.nextTick(() => {
438+
return queueMicrotask(() => {
439439
if (callback) {
440440
self.removeListener('connect', callback);
441441
callback(ex);
@@ -446,7 +446,7 @@ function doConnect(ex, self, ip, address, port, callback) {
446446
}
447447

448448
state.connectState = CONNECT_STATE_CONNECTED;
449-
process.nextTick(() => self.emit('connect'));
449+
queueMicrotask(() => self.emit('connect'));
450450
}
451451

452452

@@ -679,11 +679,11 @@ function doSend(ex, self, ip, list, address, port, callback) {
679679

680680
if (ex) {
681681
if (typeof callback === 'function') {
682-
process.nextTick(callback, ex);
682+
queueMicrotask(callback, ex);
683683
return;
684684
}
685685

686-
process.nextTick(() => self.emit('error', ex));
686+
queueMicrotask(() => self.emit('error', ex));
687687
return;
688688
} else if (!state.handle) {
689689
return;
@@ -708,14 +708,14 @@ function doSend(ex, self, ip, list, address, port, callback) {
708708
// Synchronous finish. The return code is msg_length + 1 so that we can
709709
// distinguish between synchronous success and asynchronous success.
710710
if (callback)
711-
process.nextTick(callback, null, err - 1);
711+
queueMicrotask(() => (callback, null, err - 1));
712712
return;
713713
}
714714

715715
if (err && callback) {
716716
// Don't emit as error, dgram_legacy.js compatibility
717717
const ex = exceptionWithHostPort(err, 'send', address, port);
718-
process.nextTick(callback, ex);
718+
pqueueMicrotask(() => callback(ex));
719719
}
720720
}
721721

@@ -746,7 +746,7 @@ Socket.prototype.close = function(callback) {
746746
state.handle.close();
747747
state.handle = null;
748748
defaultTriggerAsyncIdScope(this[async_id_symbol],
749-
process.nextTick,
749+
queueMicrotask,
750750
socketCloseNT,
751751
this);
752752

lib/diagnostics_channel.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function wrapStoreRun(store, data, next, transform = defaultTransform) {
8282
try {
8383
context = transform(data);
8484
} catch (err) {
85-
process.nextTick(() => {
85+
queueMicrotask(() => {
8686
triggerUncaughtException(err, false);
8787
});
8888
return next();
@@ -141,7 +141,7 @@ class ActiveChannel {
141141
const onMessage = this._subscribers[i];
142142
onMessage(data, this.name);
143143
} catch (err) {
144-
process.nextTick(() => {
144+
queueMicrotask(() => {
145145
triggerUncaughtException(err, false);
146146
});
147147
}

lib/dns.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,20 @@ function lookup(hostname, options, callback) {
194194
if (!hostname) {
195195
emitInvalidHostnameWarning(hostname);
196196
if (all) {
197-
process.nextTick(callback, null, []);
197+
queueMicrotask(() => callback(null, []));
198198
} else {
199-
process.nextTick(callback, null, null, family === 6 ? 6 : 4);
199+
queueMicrotask(() => callback(null, null, family === 6 ? 6 : 4));
200200
}
201201
return {};
202202
}
203203

204204
const matchedFamily = isIP(hostname);
205205
if (matchedFamily) {
206206
if (all) {
207-
process.nextTick(
208-
callback, null, [{ address: hostname, family: matchedFamily }]);
207+
queueMicrotask(() =>
208+
callback(null, [{ address: hostname, family: matchedFamily }]));
209209
} else {
210-
process.nextTick(callback, null, hostname, matchedFamily);
210+
queueMicrotask(() => callback(null, hostname, matchedFamily));
211211
}
212212
return {};
213213
}
@@ -222,7 +222,7 @@ function lookup(hostname, options, callback) {
222222
req, hostname, family, hints, verbatim,
223223
);
224224
if (err) {
225-
process.nextTick(callback, dnsException(err, 'getaddrinfo', hostname));
225+
queueMicrotask(() => callback(dnsException(err, 'getaddrinfo', hostname)));
226226
return {};
227227
}
228228
if (hasObserver('dns')) {

lib/events.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,9 @@ function addCatch(that, promise, type, args) {
375375

376376
if (typeof then === 'function') {
377377
then.call(promise, undefined, function(err) {
378-
// The callback is called with nextTick to avoid a follow-up
378+
// The callback is called with queueMicrotask to avoid a follow-up
379379
// rejection from this promise.
380-
process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
380+
queueMicrotask(() => emitUnhandledRejectionOrErr(that, err, type, args));
381381
});
382382
}
383383
} catch (err) {

0 commit comments

Comments
 (0)