This article brings you a detailed explanation of the event loop mechanism of the JavaScript browser. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Questions thrown in front:
How can a single thread be asynchronous?
What is the process of the event loop?
What are macrotask and microtask, and what are they? What is the difference
Single-threaded and asynchronous
When it comes to js, you will think of single-threaded and asynchronous, so how does a single-thread achieve asynchronousness? Concept first, we must first understand the relationship between single thread and asynchronous.
JS tasks are divided into two types: synchronous and asynchronous, and their processing methods are also different. Synchronous tasks are directly queued for execution on the main thread, while asynchronous tasks will be placed in the task queue. If there are multiple Tasks (asynchronous tasks) have to be queued and waiting in the task queue. The task queue is like a buffer. The task will be moved to the call stack (call stack) in the next step, and then the main thread executes the task of the call stack.
Single thread means that there is only one thread (main thread) in the js engine that is responsible for parsing and executing js code, that is, it can only do one thing at a time, and we know that for an ajax request, the main thread is waiting for its response. At the same time, it will do other things. The browser first registers the ajax callback function in the event table. After the response comes back, the callback function is added to the task queue and waits for execution. It will not cause thread blocking, so the way js handles ajax requests is is asynchronous.
In short, the process of checking whether the call stack is empty and determining which task to add to the call stack is the event loop, and the core of js's asynchronous implementation is the event loop.
Call stack and task queue
As the name suggests, the call stack is a stack structure. Function calls will form a stack frame, which contains context information such as parameters and local variables of the currently executed function. After the function is executed, its execution context is popped from the stack.
The following picture is the relationship between the call stack and the task queue
Event loop
About the event loop, Introduction to HTML specifications
There must be at least one event loop per user agent, and at most
one event loop per unit of related similar-origin browsing contexts.
An event loop has one or more task queues.
Each task is defined as coming from a specific task source.
Understood from the specification , the browser has at least one event loop, and each event loop has at least one task queue (macrotask). Each external task has its own group, and the browser will set priorities for different task groups.
macrotask & microtask
The specification mentions two concepts, but does not introduce them in detail. After consulting some information, it can be summarized as follows:
macrotask: contains the js code that executes the entire , event callbacks, XHR callbacks, timers (setTimeout/setInterval/setImmediate), IO operations, UI render
microtask: tasks that update application status, including promise callbacks, MutationObserver, process.nextTick, Object.observe
Among them, setImmediate and process.nextTick are the implementation of nodejs, which will be introduced in detail in the nodejs article.
Event processing process
Regarding the understanding of macrotask and microtask, it may be a bit obscure if you look at it this way. Combining the event loop mechanism, the understanding is much clearer. The following picture can be said to be an introduction. Very clear.
To sum up, the steps of an event loop include:
1. Check whether the macrotask queue is empty. If it is not empty, go to 2. If empty, go to 3
2. Execute a task in the macrotask
3. Continue to check whether the microtask queue is empty, if so, go to 4, otherwise go to 5
4. Take out the task in the microtask and execute Complete and return to step 3
5. Execute view update
Execution sequence of mactotask & microtask
After reading so many dry words To introduce the concept, why not look at a piece of code to get a feel for it
console.log('start') setTimeout(function() { console.log('setTimeout') }, 0) Promise.resolve().then(function() { console.log('promise1') }).then(function() { console.log('promise2') }) console.log('end')
What is the log sequence output by the print station? Based on the analysis of the above steps, is it so easy~
#First, the global code (main()) is pushed into the call stack for execution, and start is printed;
Next, setTimeout is pushed into the macrotask queue, promise.then callback is put into the microtask queue, and finally console.log('end') is executed to print out end;
At this point, the code in the call stack has been executed. Looking back at the definition of macrotask, we know that the global code belongs to macrotask. After the macrotask is executed, the next step is to execute the task of the microtask queue. Execute the promise callback and print promise1;
promise回调函数默认返回undefined,promise状态变为fullfill触发接下来的then回调,继续压入microtask队列,event loop会把当前的microtask队列一直执行完,此时执行第二个promise.then回调打印出promise2;
这时microtask队列已经为空,从上面的流程图可以知道,接下来主线程会去做一些UI渲染工作(不一定会做),然后开始下一轮event loop,执行setTimeout的回调,打印出setTimeout;
这个过程会不断重复,也就是所谓的事件循环。
视图渲染的时机
回顾上面的事件循环示意图,update rendering(视图渲染)发生在本轮事件循环的microtask队列被执行完之后,也就是说执行任务的耗时会影响视图渲染的时机。通常浏览器以每秒60帧(60fps)的速率刷新页面,据说这个帧率最适合人眼交互,大概16.7ms渲染一帧,所以如果要让用户觉得顺畅,单个macrotask及它相关的所有microtask最好能在16.7ms内完成。
但也不是每轮事件循环都会执行视图更新,浏览器有自己的优化策略,例如把几次的视图更新累积到一起重绘,重绘之前会通知requestAnimationFrame执行回调函数,也就是说requestAnimationFrame回调的执行时机是在一次或多次事件循环的UI render阶段。
以下代码可以验证
setTimeout(function() {console.log('timer1')}, 0) requestAnimationFrame(function(){ console.log('requestAnimationFrame') }) setTimeout(function() {console.log('timer2')}, 0) new Promise(function executor(resolve) { console.log('promise 1') resolve() console.log('promise 2') }).then(function() { console.log('promise then') }) console.log('end')
可以看到,结果1中requestAnimationFrame()是在一次事件循环后执行,而在结果2,它的执行则是在三次事件循环结束后。
总结
1、事件循环是js实现异步的核心
2、每轮事件循环分为3个步骤:
a) 执行macrotask队列的一个任务
b) 执行完当前microtask队列的所有任务
c) UI render
3、浏览器只保证requestAnimationFrame的回调在重绘之前执行,没有确定的时间,何时重绘由浏览器决定
The above is the detailed content of Detailed explanation of the event loop mechanism of javascript browser. For more information, please follow other related articles on the PHP Chinese website!