Loading...
save question

How does the JavaScript event loop handle asynchronous operations like setTimeout and Promises?

clock icon

asked 2 months ago

message icon

1

eye icon

85

I'm trying to understand how the JavaScript event loop works under the hood. Specifically, I'm confused about the order of execution when mixing setTimeout, Promises, and regular synchronous code.

For example, given this code:

1console.log('1')
2
3setTimeout(() => console.log('2'), 0)
4
5Promise.resolve().then(() => console.log('3'))
6
7console.log('4')
1console.log('1')
2
3setTimeout(() => console.log('2'), 0)
4
5Promise.resolve().then(() => console.log('3'))
6
7console.log('4')

I expected the output to be 1, 2, 3, 4 but the actual output is 1, 4, 3, 2. Can someone explain why this happens and how the call stack, task queue, and microtask queue interact with each other?

1 Answer

Introduction to the JavaScript Event Loop

The JavaScript event loop is a crucial component that handles asynchronous operations, allowing the language to perform non-blocking I/O operations. It consists of the call stack, task queue (also known as the macrotask queue), and microtask queue.

Call Stack, Task Queue, and Microtask Queue

  • The call stack is a region of memory that stores information about the active subroutines of a program. It's where the current execution context is stored.
  • The task queue (or macrotask queue) is where tasks like setTimeout are stored. These tasks are executed one by one when the call stack is empty.
  • The microtask queue stores microtasks like Promises. Microtasks are executed after each task in the task queue.

Execution Order

Given the provided code:

1console.log('1')
2// (1) Synchronous code is executed first
3setTimeout(() => console.log('2'), 0)
4// (2) setTimeout adds a task to the task queue
5Promise.resolve().then(() => console.log('3'))
6// (3) Promise resolves and adds a microtask to the microtask queue
7console.log('4')
8// (4) Synchronous code is executed
1console.log('1')
2// (1) Synchronous code is executed first
3setTimeout(() => console.log('2'), 0)
4// (2) setTimeout adds a task to the task queue
5Promise.resolve().then(() => console.log('3'))
6// (3) Promise resolves and adds a microtask to the microtask queue
7console.log('4')
8// (4) Synchronous code is executed

The order of execution is as follows:

  1. Synchronous code (console.log('1') and console.log('4')) is executed first, resulting in the output 1 and 4.
  2. After the call stack is empty, the event loop checks the microtask queue and executes any available microtasks. In this case, it's the Promise.resolve().then() callback, resulting in the output 3.
  3. Finally, the event loop checks the task queue and executes any available tasks. Since the setTimeout callback is in the task queue, it's executed last, resulting in the output 2.

Conclusion

In summary, the order of execution when mixing setTimeout, Promises, and synchronous code is as follows:

  • Synchronous code is executed first.
  • Microtasks (like Promises) are executed after the call stack is empty.
  • Tasks (like setTimeout) are executed last, one by one, from the task queue.

This results in the actual output being 1, 4, 3, 2. Understanding the event loop and the call stack, task queue, and microtask queue is essential to grasping how asynchronous operations work in JavaScript.

Code Example

To further illustrate this concept, consider the following example:

1console.log('1')
2
3setTimeout(() => {
4 console.log('5')
5 Promise.resolve().then(() => console.log('6'))
6}, 0)
7
8Promise.resolve().then(() => console.log('2'))
9
10setTimeout(() => console.log('3'), 0)
11
12console.log('4')
1console.log('1')
2
3setTimeout(() => {
4 console.log('5')
5 Promise.resolve().then(() => console.log('6'))
6}, 0)
7
8Promise.resolve().then(() => console.log('2'))
9
10setTimeout(() => console.log('3'), 0)
11
12console.log('4')

In this example, the order of execution will be:

  1. console.log('1') and console.log('4') (synchronous code)
  2. Promise.resolve().then() callback (microtask) resulting in console.log('2')
  3. setTimeout callbacks (tasks) resulting in console.log('5') and then console.log('3')
  4. Finally, the Promise.resolve().then() callback inside the setTimeout resulting in console.log('6')

The resulting output will be 1, 4, 2, 5, 3, 6. This demonstrates how the event loop handles asynchronous operations and the order in which tasks and microtasks are executed.

1

Write your answer here

Provide a detailed answer to help solve the question.

Top Questions