Skip to content

Commit 1a66c0d

Browse files
committed
process: code cleanup for nextTick
Fix a few edge cases and non-obvious issues with nextTick: 1. Emit destroy hook in a try-finally rather than triggering it before the callback runs. 2. Re-word comment for processPromiseRejections and make sure it returns true in the rejectionHandled case too. 3. Small readability improvements.
1 parent ff4a71c commit 1a66c0d

File tree

2 files changed

+24
-29
lines changed

2 files changed

+24
-29
lines changed

lib/internal/process/promises.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,19 @@ function emitDeprecationWarning() {
139139
}
140140
}
141141

142-
// If this method returns true, at least one more tick need to be
143-
// scheduled to process any potential pending rejections
142+
// If this method returns true, we've executed user code or triggered
143+
// a warning to be emitted which requires the microtask and next tick
144+
// queues to be drained again.
144145
function processPromiseRejections() {
146+
let maybeScheduledTicksOrMicrotasks = asyncHandledRejections.length > 0;
147+
145148
while (asyncHandledRejections.length > 0) {
146149
const { promise, warning } = asyncHandledRejections.shift();
147150
if (!process.emit('rejectionHandled', promise)) {
148151
process.emitWarning(warning);
149152
}
150153
}
151154

152-
let maybeScheduledTicks = false;
153155
let len = pendingUnhandledRejections.length;
154156
while (len--) {
155157
const promise = pendingUnhandledRejections.shift();
@@ -167,9 +169,10 @@ function processPromiseRejections() {
167169
state === states.warn) {
168170
emitWarning(uid, reason);
169171
}
170-
maybeScheduledTicks = true;
172+
maybeScheduledTicksOrMicrotasks = true;
171173
}
172-
return maybeScheduledTicks || pendingUnhandledRejections.length !== 0;
174+
return maybeScheduledTicksOrMicrotasks ||
175+
pendingUnhandledRejections.length !== 0;
173176
}
174177

175178
function getError(name, message) {

lib/internal/process/task_queues.js

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,46 +65,38 @@ function processTicksAndRejections() {
6565
while (tock = queue.shift()) {
6666
const asyncId = tock[async_id_symbol];
6767
emitBefore(asyncId, tock[trigger_async_id_symbol]);
68-
// emitDestroy() places the async_id_symbol into an asynchronous queue
69-
// that calls the destroy callback in the future. It's called before
70-
// calling tock.callback so destroy will be called even if the callback
71-
// throws an exception that is handled by 'uncaughtException' or a
72-
// domain.
73-
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
74-
// that nextTick() doesn't allow the event loop to proceed, but if
75-
// any async hooks are enabled during the callback's execution then
76-
// this tock's after hook will be called, but not its destroy hook.
77-
if (destroyHooksExist())
78-
emitDestroy(asyncId);
79-
80-
const callback = tock.callback;
81-
if (tock.args === undefined)
82-
callback();
83-
else
84-
callback(...tock.args);
68+
69+
try {
70+
const callback = tock.callback;
71+
if (tock.args === undefined)
72+
callback();
73+
else
74+
callback(...tock.args);
75+
} finally {
76+
if (destroyHooksExist())
77+
emitDestroy(asyncId);
78+
}
8579

8680
emitAfter(asyncId);
8781
}
88-
setHasTickScheduled(false);
8982
runMicrotasks();
9083
} while (!queue.isEmpty() || processPromiseRejections());
84+
setHasTickScheduled(false);
9185
setHasRejectionToWarn(false);
9286
}
9387

9488
class TickObject {
95-
constructor(callback, args, triggerAsyncId) {
89+
constructor(callback, args) {
9690
this.callback = callback;
9791
this.args = args;
9892

9993
const asyncId = newAsyncId();
94+
const triggerAsyncId = getDefaultTriggerAsyncId();
10095
this[async_id_symbol] = asyncId;
10196
this[trigger_async_id_symbol] = triggerAsyncId;
10297

10398
if (initHooksExist()) {
104-
emitInit(asyncId,
105-
'TickObject',
106-
triggerAsyncId,
107-
this);
99+
emitInit(asyncId, 'TickObject', triggerAsyncId, this);
108100
}
109101
}
110102
}
@@ -132,7 +124,7 @@ function nextTick(callback) {
132124

133125
if (queue.isEmpty())
134126
setHasTickScheduled(true);
135-
queue.push(new TickObject(callback, args, getDefaultTriggerAsyncId()));
127+
queue.push(new TickObject(callback, args));
136128
}
137129

138130
let AsyncResource;

0 commit comments

Comments
 (0)