-
Notifications
You must be signed in to change notification settings - Fork 6.3k
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
fix: Improved understanding of setImmediate and setTimeout output #6994
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@mikeesto Can you look into this PR? |
apps/site/pages/en/learn/asynchronous-work/understanding-processnexttick.md
Outdated
Show resolved
Hide resolved
apps/site/pages/en/learn/asynchronous-work/understanding-processnexttick.md
Outdated
Show resolved
Hide resolved
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.
Looks good!
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.
I tried to reproduce with the following script:
import assert from 'node:assert'
import {spawn} from 'node:child_process'
let _1223344 = 0;
let _1224433 = 0;
let other = 0
for (let i = 0; i < 999; i++) {
const cp = spawn(process.execPath, ['--input-type', process.argv[2], '-e', `console.log('Hello => number 1');
setImmediate(() => {
console.log('Running before the timeout => number 3');
console.log('Running setImmediate in the check phase => number 3');
});
setTimeout(() => {
console.log('The timeout running last => number 4');
console.log('Running setTimeout in the timers phase => number 4');
}, 0);
process.nextTick(() => {
console.log('Running at next tick => number 2');
console.log('Running process.nextTick in the nextTick queue => number 2');
});`]);
cp.stdout.toArray().then(stdout => {
stdout = Buffer.concat(stdout).toString();
const numbers = stdout.trim().split('\n').map(line => Number(line.at(-1)));
try {
assert.deepStrictEqual(numbers, [1, 2, 2, 3, 3, 4, 4]);
_1223344++;
return;
} catch {}
try {
assert.deepStrictEqual(numbers, [1, 2, 2, 4, 4, 3, 3]);
_1224433++;
return;
} catch {}
console.log({ stdout, numbers });
other++;
});
}
process.on('exit', () => {
console.log({ _1223344, _1224433, other });
});
Here are the results:
$ node repro.mjs commonjs
{ _1223344: 472, _1224433: 527, other: 0 }
$ node repro.mjs module
{ _1223344: 311, _1224433: 688, other: 0 }
As you can see, the proportions are different in ESM vs CJS, but it seems very wrong to state that ESM output is guarenteed.
import assert from 'node:assert'
let order1223344 = 0;
let order1224433 = 0;
let other = 0
const totalRuns = 999
function runTest() {
return new Promise((resolve) => {
const output = [];
console.log('Hello => number 1');
output.push(1);
setImmediate(() => {
console.log('Running before the timeout => number 3');
console.log('Running setImmediate in the check phase => number 3');
output.push(3);
output.push(3);
if (output.length === 7) resolve(output);
});
setTimeout(() => {
console.log('The timeout running last => number 4');
console.log('Running setTimeout in the timers phase => number 4');
output.push(4);
output.push(4);
if (output.length === 7) resolve(output);
}, 1);
process.nextTick(() => {
console.log('Running at next tick => number 2');
console.log('Running process.nextTick in the nextTick queue => number 2');
output.push(2);
output.push(2);
});
});
}
for (let i = 0; i < totalRuns; i++) {
runTest().then(numbers => {
try {
assert.deepStrictEqual(numbers, [1, 2, 2, 3, 3, 4, 4]);
order1223344++;
} catch {
try {
assert.deepStrictEqual(numbers, [1, 2, 2, 4, 4, 3, 3]);
order1224433++;
} catch {
console.log({ numbers });
other++;
}
}
if (i === totalRuns -1) {
console.log({ order1223344, order1224433, other });
}
});
} I feel that the above output you got is because Spawning childs may run like a CJS script while in module it is asynchronous. |
It seems to me it has nothing to do with ESM or CJS, rather how busy is the event loop. Anyway, it's good to know that sometimes it's deterministic I guess, but as I've shown in #6994 (review), it's not always the case. |
Description
In the understanding-processnexttick.md file we have code example output for setImmediate and setTimeout. While that output holds true for ES modules case and have indeterministic behaviour in CommonJS case. I mentioned it to improve the understanding, incase a person tries to run the code and get confused.
Validation
Check List
npm run format
to ensure the code follows the style guide.npm run test
to check if all tests are passing.npx turbo build
to check if the website builds without errors.