Although this question is 7 years old, it's still relevant:
Solution:
- The Promise block operates synchronously
- The Promise callback blocks (resolve, reject) are synchronous
- The
call
callback (not the block itself) can be asynchronous if a setTimeout block is used within the Promise block
Therefore, to directly address Jane Wayne's inquiry, using a setTimeout block will render the Promise block non-blocking.
Clarification:
Javascript functions synchronously with just one thread. The Javascript engine employs a stack called the Call Stack to execute tasks in Last In First Out (LIFO) order.
On another note, there exist Web APIs within the browser environment, such as setTimeout
found within the Window
object.
Communication between these two worlds is facilitated by two queues - Microtask and Macrotask queues, that follow a First In First Out (FIFO) model, managed by the Event Loop running continuously.
Event Loop, Microtask and Macrotask queues
The process unfolds as follows:
- Execute all script through the Call Stack
- The Event Loop constantly checks if the Call Stack is empty
- If an empty Call Stack is detected, callbacks from the Microtask queue are pushed to the Call Stack
- Once the Microtask queue is cleared, the Event Loop works on the Macrotask queue
Now, delving into the world of Promises:
- The Promise block executes synchronously
- Within the Promise block, anything executed outside the Javascript engine (like a
setTimeout
block) is deemed asynchronous since it runs outside the main Javascript thread.
- While the execution of Promise callbacks (
resolve
and reject
) may seem asynchronous, the core block remains synchronous
Hence, without any asynchronous components inside the Promise block or its callbacks, the Promise tends to halt your execution.
For instance:
console.log(`Start of file`)
const fourSeconds = 4000
// Non-blocking execution due to being added to the Macrotask queue
// Microtasks take precedence so all Promises without setTimeout go first
setTimeout(() => {
console.log(`Callback 1`)
})
// Blocking execution because the Promise block is synchronous
// However, the result will only show after the Call Stack clears
// This becomes the first Microtask printed
new Promise((resolve, reject) => {
let start = Date.now()
while (Date.now() - start <= fourSeconds) { }
resolve(`Promise block resolved`)
}).then(result => console.log(result))
// Non-blocking execution, goes to the Macrotask queue due to setTimeout
// Will pause all subsequent Macrotasks in the queue
// In this scenario, it pauses the printing of Macrotask "Callback 2"
new Promise((resolve, reject) => {
setTimeout(() => resolve(`Promise callback block resolved`))
}).then(result => {
let start = Date.now()
while (Date.now() - start <= fourSeconds) { }
console.log(result)
})
// Non-blocking execution, added to the Macrotask queue
// Microtasks hold priority over it
// Previous Macrotasks also delay its execution, hence it is last to run
setTimeout(() => {
console.log(`Callback 2`)
})
// Non-blocking execution, enters the Microtask queue overriding setTimeout
// Previous microtasks have prime importance
Promise.resolve(`Simply resolved`).then(result => console.log(result))
console.log(`End of file`)
/*
Output:
Start of file
[... execution blocked 4 seconds ...]
End of file
Promise block resolved
Simply resolved
Callback 1
[... execution blocked 4 seconds ...]
Promise callback block resolved
Callback 2
*/