Skip to content

Commit 4d82fd8

Browse files
committed
process: improve nextTick performance
No longer check process.exiting on each tick add. Cleaner emitDestroy with try/finally. No longer use Reflect.apply since it's slower.
1 parent 528d100 commit 4d82fd8

File tree

1 file changed

+31
-36
lines changed

1 file changed

+31
-36
lines changed

lib/internal/process/task_queues.js

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { FunctionPrototype, Reflect } = primordials;
3+
const { FunctionPrototype } = primordials;
44

55
const {
66
// For easy access to the nextTick state in the C++ land,
@@ -62,25 +62,24 @@ function processTicksAndRejections() {
6262
let tock;
6363
do {
6464
while (tock = queue.shift()) {
65-
const asyncId = tock[async_id_symbol];
66-
emitBefore(asyncId, tock[trigger_async_id_symbol]);
67-
// emitDestroy() places the async_id_symbol into an asynchronous queue
68-
// that calls the destroy callback in the future. It's called before
69-
// calling tock.callback so destroy will be called even if the callback
70-
// throws an exception that is handled by 'uncaughtException' or a
71-
// domain.
72-
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
73-
// that nextTick() doesn't allow the event loop to proceed, but if
74-
// any async hooks are enabled during the callback's execution then
75-
// this tock's after hook will be called, but not its destroy hook.
76-
if (destroyHooksExist())
77-
emitDestroy(asyncId);
78-
79-
const callback = tock.callback;
80-
if (tock.args === undefined)
81-
callback();
82-
else
83-
Reflect.apply(callback, undefined, tock.args);
65+
const {
66+
[async_id_symbol]: asyncId,
67+
[trigger_async_id_symbol]: triggerAsyncId,
68+
callback,
69+
args
70+
} = tock;
71+
72+
emitBefore(asyncId, triggerAsyncId);
73+
74+
try {
75+
if (args === undefined)
76+
callback();
77+
else
78+
callback(...args);
79+
} finally {
80+
if (destroyHooksExist())
81+
emitDestroy(asyncId);
82+
}
8483

8584
emitAfter(asyncId);
8685
}
@@ -91,23 +90,18 @@ function processTicksAndRejections() {
9190
}
9291

9392
class TickObject {
94-
constructor(callback, args, triggerAsyncId) {
93+
constructor(callback, args, asyncId, triggerAsyncId) {
9594
// This must be set to null first to avoid function tracking
96-
// on the hidden class, revisit in V8 versions after 6.2
95+
// on the hidden class, revisit in V8 versions after 7.4
9796
this.callback = null;
9897
this.callback = callback;
9998
this.args = args;
10099

101-
const asyncId = newAsyncId();
102100
this[async_id_symbol] = asyncId;
103101
this[trigger_async_id_symbol] = triggerAsyncId;
104102

105-
if (initHooksExist()) {
106-
emitInit(asyncId,
107-
'TickObject',
108-
triggerAsyncId,
109-
this);
110-
}
103+
if (initHooksExist())
104+
emitInit(asyncId, 'TickObject', triggerAsyncId, this);
111105
}
112106
}
113107

@@ -117,24 +111,25 @@ function nextTick(callback) {
117111
if (typeof callback !== 'function')
118112
throw new ERR_INVALID_CALLBACK(callback);
119113

120-
if (process._exiting)
121-
return;
122-
123-
var args;
114+
let args;
124115
switch (arguments.length) {
125116
case 1: break;
126117
case 2: args = [arguments[1]]; break;
127118
case 3: args = [arguments[1], arguments[2]]; break;
128119
case 4: args = [arguments[1], arguments[2], arguments[3]]; break;
129120
default:
130121
args = new Array(arguments.length - 1);
131-
for (var i = 1; i < arguments.length; i++)
122+
for (let i = 1; i < arguments.length; i++)
132123
args[i - 1] = arguments[i];
133124
}
134125

135-
if (queue.isEmpty())
126+
if (queue.isEmpty() && !process._exiting)
136127
setHasTickScheduled(true);
137-
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
128+
129+
queue.push(new TickObject(callback,
130+
args,
131+
newAsyncId(),
132+
getDefaultTriggerAsyncId()));
138133
}
139134

140135
let AsyncResource;

0 commit comments

Comments
 (0)