Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setTimeout / Promise.resolve: Macrotask vs Microtask

I've been introduced to the concepts of Microtasks and Macrotasks for a while now, and from everything I've read, I always thought setTimeout to be considered to create a macrotask and Promise.resolve() (or process.nextTick on NodeJS) to create microtasks.

(Yes, I'm aware that different Promise libraries like Q and Bluebird have different schedulers implementations, but here I'm referring to the native Promises on each platform)

With this in mind I'm unable to explain the following sequence of events on NodeJS (results on Chrome are different from NodeJS (both v8 LTS and v10) and match with my understanding on this subject).

for (let i = 0; i < 2; i++) {
	setTimeout(() => {
		console.log("Timeout ", i);
		Promise.resolve().then(() => {
			console.log("Promise 1 ", i);
		}).then(() => {
			console.log("Promise 2 ", i);
		});
	})
}

So, the results I have on Chrome (and that are consistent with my understanding of Micro/Macro tasks and how Promise.resolve and setTimeout behave) are:

Timeout  0
Promise 1  0
Promise 2  0
Timeout  1
Promise 1  1
Promise 2  1

The same code executed on NodeJS outputs:

Timeout  0
Timeout  1
Promise 1  0
Promise 2  0
Promise 1  1
Promise 2  1

I'm looking for a way to have the same results on NodeJS that I have on Chrome. I've also tested with process.nextTick instead of Promise.resolve() but the results are the same.

Can anyone point me into the right direction?

like image 287
jpsfs Avatar asked Aug 10 '18 20:08

jpsfs


People also ask

Is setTimeout a microtask?

The certain way, is to look up the spec. For instance, step 14 of setTimeout queues a task, whereas step 5 of queuing a mutation record queues a microtask. As mentioned, in ECMAScript land, they call microtasks "jobs".

Is setTimeout a Macrotask?

Macro tasks include keyboard events, mouse events, timer events (setTimeout) , network events, Html parsing, changing Urletc. A macro task represents some discrete and independent work. micro task queue has higher priority so macro task will wait for all the micro tasks are executed first.

Is promise a microtask?

In the context of native promises, a promise callback is considered as a microtask and queued in a microtask queue which will be processed right after the next tick queue. Consider the following example. In the above example, the following actions will happen.

What is Macrotask in event loop?

Macro-tasks within an event loop: Macro-task represents some discrete and independent work. These are always the execution of the JavaScript code and micro-task queue is empty. Macro-task queue is often considered the same as the task queue or the event queue.


2 Answers

This was recognized by the NodeJs team as a bug, more details here: https://github.com/nodejs/node/issues/22257

Meantime it was already fixed and released has part of Node v11.

Best, José

like image 79
jpsfs Avatar answered Oct 02 '22 18:10

jpsfs


You can't control how different architectures queue the promises and timeouts.

Excellent Read Here: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

If you want the same results you are going to have to chain promises.

let chain = Promise.resolve(null)

for (let i = 0; i < 2; i++) {
  console.log("Chaining ", i);
  chain = chain.then(() => Promise.resolve()
    .then(() => {
      setTimeout(() => {
        console.log("Timeout ", i);

        Promise.resolve()
          .then(() => {
            console.log("Promise 1 ", i);
          })
          .then(() => {
            console.log("Promise 2 ", i);
          })

      }, 0)
    }))
}

chain.then(() => console.log('done'))
like image 26
Steven Spungin Avatar answered Oct 02 '22 20:10

Steven Spungin