Skip to content
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

legendAhsan
Copy link

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

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run npm run format to ensure the code follows the style guide.
  • I have run npm run test to check if all tests are passing.
  • I have run npx turbo build to check if the website builds without errors.
  • [] I've covered new added functionality with unit tests if necessary. - Not required

@legendAhsan legendAhsan requested a review from a team as a code owner August 20, 2024 00:26
Copy link

vercel bot commented Aug 20, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
nodejs-org ✅ Ready (Inspect) Visit Preview Sep 13, 2024 11:42am

@legendAhsan
Copy link
Author

@mikeesto Can you look into this PR?

Copy link

@programORdie2 programORdie2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Copy link
Contributor

@aduh95 aduh95 left a 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.

@legendAhsan
Copy link
Author

legendAhsan commented Oct 8, 2024

@aduh95

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.
Put the above code in module file like common.mjs and you will see that it will only increment 1223344 variable.

@aduh95
Copy link
Contributor

aduh95 commented Nov 6, 2024

I feel that the above output you got is because Spawning childs may run like a CJS script while in module it is asynchronous.
Put the above code in module file like common.mjs and you will see that it will only increment 1223344 variable.

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.

@bmuenzenmeyer bmuenzenmeyer added the learn Issues/pr concerning the learn section label Nov 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
learn Issues/pr concerning the learn section
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants