The original
We know that JS is executed in a single thread, so how does asynchronous code JS handle? For example, the following code would output:
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
new Promise(function(resolve) {
console.log(3);
resolve(Date.now());
}).then(function() {
console.log(4);
});
console.log(5);
setTimeout(function() {
new Promise(function(resolve) {
console.log(6);
resolve(Date.now());
}).then(function() {
console.log(7);
});
}, 0);
Copy the code
In the case of not running we can guess the final output and then expand on what we are going to say.
1. Macro and micro tasks
Based on our years of experience writing Ajax, JS should be executed in the order of statements, making an asynchronous request and then executing it when the asynchronous result is returned. But how does he manage these operations internally?
In JS, tasks are divided into macroTask and microtask. These two tasks maintain a queue respectively and are executed using a first-in, first-out strategy! All synchronized tasks are executed on macro tasks.
Macro tasks include: Script (overall code), setTimeout, setInterval, I/O, UI interaction events, postMessage, MessageChannel, setImmediate(node.js environment). Microtasks mainly include promise. then, MutationObserver, and Process.nexttick (node.js environment).
The specific operation steps are as follows:
- Retrieves a task from the head of a macro task to execute;
- Add microtasks to the queue if they are encountered during execution.
- After the execution of the macro task, whether there are tasks in the queue of the micro task, if there are tasks, execute them one by one until the execution is completed;
- GUI rendering;
- Go back to Step 1 until the macro task completes;
These four steps form an eventloop, called an eventloop.
Back to the code we talked about above:
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
new Promise(function(resolve) {
console.log(3);
resolve(Date.now());
}).then(function() {
console.log(4);
});
console.log(5);
setTimeout(function() {
new Promise(function(resolve) {
console.log(6);
resolve(Date.now());
}).then(function() {
console.log(7);
});
}, 0);
Copy the code
Perform the following steps:
1. Run log(1) to output 1. 2. When setTimeout is encountered, add the code log(2) of the callback to the macro task for execution; 3. Run console.log(3) to add log(4) from then to the microtask. 4. Run log(5) to output 5. 5. When setTimeout is encountered, add the code log(6, 7) of the callback to the macro task; 6. After one task of the macro task is executed, check whether there is a task in the microtask queue. If there is a microtask log(4) (added in Step 3), perform output 4. 7. Take out the next macro task log(2) to execute, output 2; 8. After one task of the macro task is executed, check whether any task exists in the microtask queue. 9. Execute the next macro task, execute log(6), add the log(7) of then to the microtask; 10. After the macro task is executed, a microtask log(7) (added in Step 9) exists. Therefore, the final output sequence is: 1, 3, 5, 4, 2, 6, 7;
We implement a slightly more time-consuming operation in promise.then, and this step becomes even more obvious:
console.log(1);
var start = Date.now();
setTimeout(function() {
console.log(2);
}, 0);
setTimeout(function() {
console.log(4.Date.now() - start);
}, 400);
Promise.resolve().then(function() {
var sum = function(a, b) {
return Number(a) + Number(b);
}
var res = [];
for(var i=0; i<5000000; i++) {
var a = Math.floor(Math.random()*100);
var b = Math.floor(Math.random()*200);
res.push(sum(a, b));
}
res = res.sort();
console.log(3);
})
Copy the code
Promise.then, mister forms an array of 5 million random numbers and sorts the array. Run this code and you’ll see that it prints 1 immediately, 3 a little later, and then 2. No matter how long you wait to print 3, 2 will always print after 3. This confirms that step 3 in eventLoop must wait for all microtasks to complete before starting the next macro task.
In the meantime, the output of this code is interesting:
setTimeout(function() {
console.log(4.Date.now() - start); // 4, 1380 The output time difference varies with the state of the computer
}, 400);
Copy the code
Originally, the output was set to be 400ms later, but due to the serious time consuming of the previous task, the subsequent task could only be postponed to the later one. It also shows that the delay of setTimeout and setInterval operations is not accurate. These two methods can only be used for macro tasks after 400ms, but the specific execution time depends on whether the thread is free. If there are time-consuming operations in the previous task, or an infinite number of microtasks are added, the next task will be blocked.
2.async-await
We can also see from the above code that the promise. then code is a microservice, so how to execute the async-await code? For example:
function A() {
return Promise.resolve(Date.now());
}
async function B() {
console.log(Math.random());
let now = await A();
console.log(now);
}
console.log(1);
B();
console.log(2);
Copy the code
In fact, async-await is just a syntactic candy for Promise+ Generator. Let’s rewrite the above code to make it a little clearer:
function B() {
console.log(Math.random());
A().then(function(now) {
console.log(now); })}console.log(1);
B();
console.log(2);
Copy the code
1, 0.4793526730678652(random number), 2, 1557830834679(timestamp);
3. requestAnimationFrame
RequestAnimationFrame is also a method that executes asynchronously, but it is neither a macro task nor a microtask. As defined in MDN:
Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method takes a callback function as an argument, which is executed before the next browser redraw before the GUI rendering, but after the microservice, but requestAnimationFrame doesn’t necessarily have to be executed during the current frame, It is up to the browser to decide which frame to execute based on the current policy.
4. To summarize
The two most important things to remember are that JS is a single-threaded and eventloop loop.