radinvafaei
radinvafaei

Reputation: 75

JS event loop priority

I run this code on Nodejs and I expect that got an error but it worked! If I set 2500 ms to setTimeout I got an error and it’s normal. Is there anybody here to explain it for me? And why I see IIFE log first?

I run this code on browser and got error as I expected.

const data = [{}]
const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([1,2,3,4]);
    }, 3000);
  });

console.log('hiiii');

setTimeout(() => {
    data[0].map(e => console.log('to ',e))
  }, 2999);
(async() => {
     data[0] = await myPromise
     data[0].map(e => console.log('iife ',e))
    }
)()

console.log('byeeeee')

The below code is what I see on my console

hiiii
byeeeee
iife 1
iife 2
iife 3
iife 4
to 1
to 2
to 3
to 4

Upvotes: 3

Views: 1299

Answers (2)

Jyotirmoy Upadhaya
Jyotirmoy Upadhaya

Reputation: 545

The questions related to asynchronous JavaScript can be answered by investigating the event loop only.

How asynchronous JavaScript works?

  • The call stack executes the functions. Web APIs is the place the async operations (fetch requests, promises, timers) with their callbacks are waiting to complete.
  • The task queue (also named macrostasks) is a FIFO (First In, First Out) structure that holds the callbacks of async operations that are ready to be executed.
  • The job queue (also named microtasks) is a FIFO (First In, First Out) structure that holds the callbacks of promises that are ready to be executed.
  • Finally, the event loop permanently monitors whether the call stack is empty. If the call stack is empty, the event loop looks into the job queue or task queue, and dequeues any callback ready to be executed into the call stack.

Let's take an example:

setTimeout(function timeout() 
{
    console.log('Set timeout finished!');
}, 0);

Promise.resolve(1).then(function resolve() 
{
   console.log('Resolved!');
});

The immediately resolved promise is processed before an immediate timeout. Because of the event loop priorities dequeuing jobs from the job queue (which stores the fulfilled promises' callbacks) over the tasks from the task queue (which stores timed out setTimeout() callbacks).

Upvotes: 1

Kevin Perry
Kevin Perry

Reputation: 541

If line 10 happens to execute at least 1 ms after line 2, then its callback will get queued to run after the callback with the resolve() (and hence after the "iife" logging, which happens in the aftermath of the resolve()).

The solution is to move the second setTimeout() before the first one, to ensure their callbacks are queued in the expected order. So, for instance, this code:

const data = [[99]];
setTimeout(() => {
    data[0].forEach(e => console.log('to ',e))
  }, 2999);
const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([1,2,3,4]);
    }, 3000);
  });

console.log('hiiii');

(async() => {
     data[0] = await myPromise
     data[0].forEach(e => console.log('iife ',e))
    }
)();

console.log('byeeeee');

reliably produces this result:

hiiii
byeeeee
to  99
iife  1
iife  2
iife  3
iife  4

The logic of the event loop is such that calling setTimeout will queue its callback to become runnable x milliseconds after the current time. Once that amount of time passes, it then has to wait for whatever task is currently executing to finish, and then wait for any other tasks that got queued ahead of it, before it can actually run.

Not sure what you're trying to do, but you'll never get the "to 1" line to appear before the "iife 4" line, because everything from setting data[0] to logging "iife 4" happens inside a single, synchronous chunk of code.

Upvotes: 3

Related Questions