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

queueMicrotask() is called before process.nextTick() in ESM #47319

Closed
deokjinkim opened this issue Mar 30, 2023 · 4 comments
Closed

queueMicrotask() is called before process.nextTick() in ESM #47319

deokjinkim opened this issue Mar 30, 2023 · 4 comments
Labels
process Issues and PRs related to the process subsystem.

Comments

@deokjinkim
Copy link
Contributor

deokjinkim commented Mar 30, 2023

Version

v20.0.0-pre

Platform

Linux deokjinkim-MS-7885 5.15.0-67-generic #74-Ubuntu SMP Wed Feb 22 14:14:39 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

process

What steps will reproduce the bug?

When I run example of ESM in document, actual result is different from expected result.
https://nodejs.org/dist/latest-v19.x/docs/api/process.html#when-to-use-queuemicrotask-vs-processnexttick

import { nextTick } from 'node:process';

Promise.resolve().then(() => console.log(2));
queueMicrotask(() => console.log(3));
nextTick(() => console.log(1));
// Output:
// 1
// 2
// 3

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

1
2
3

What do you see instead?

2
3
1

Additional information

In example of CJS, actual result is same to expected result.

const { nextTick } = require('node:process');

Promise.resolve().then(() => console.log(2));
queueMicrotask(() => console.log(3));
nextTick(() => console.log(1));
// Output:
// 1
// 2
// 3
@deokjinkim deokjinkim added the process Issues and PRs related to the process subsystem. label Mar 30, 2023
@jasnell
Copy link
Member

jasnell commented Mar 30, 2023

Yes, this is a known and correct behavior due to the fact that an ESM entry point is run after the event loop starts and is processed as part of the microtask queue. It's based on the internal ordering of microtasks vs. next ticks.

With CommonJS, scripts are run as a function. That function queues one next tick and one microtask. When the stack unwinds back to c++, we first drain the next tick queue followed by the microtask queue, which means the next tick callback is invoked first, leading to the order (1, 2, 3).

With ESM, there is a promise, and the code is loaded within the scope of a microtask. Since the code schedules another microtask while we are in the process of draining the queue, we end up calling the queued microtasks first (so we get the 2 and 3), and only after that stack unwinds back to c++ do we drain the pending next tick queue (getting the 1).

I'm glossing over a number of details here but what you're seeing here is absolutely expected behavior that is definitely not a bug.

@jasnell jasnell closed this as completed Mar 30, 2023
@deokjinkim
Copy link
Contributor Author

@jasnell Thank you for kind explanation. Then, do we need to update example of ESM because (1, 2, 3) is expected in document for now? According to your explanation, (2, 3, 1) is expected for example of ESM in below document.
https://nodejs.org/dist/latest-v19.x/docs/api/process.html#when-to-use-queuemicrotask-vs-processnexttick

@climba03003
Copy link
Contributor

Duplicate of #45048
PR that stale #45093 for fixing the example

@jasnell
Copy link
Member

jasnell commented Mar 30, 2023

Yeah that example needs to be updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
process Issues and PRs related to the process subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants