event loop
Browser kernel is multi-threaded,
Javascript
single-threaded.
The landlord explained prior js
asynchronous processing, just to explain a method of asynchronous processing, but for the principle of asynchronous processing is still not very understanding, this article is for threading of the browser JavaScript
to analyze operational mechanism.
1: Basic Browser
We often say that js
the environment is a single-threaded execution, executes the code in order, but JavaScript
they can be asynchronous, both feel there is a conflict, but essentially, if you understand the browser's event loop mechanism (event loop), it will that does not conflict.
Only browser which not only explain JavaScript
the engine, also includes many other engines.
Browser include:
- User Interface: including the address bar, forward / back buttons, bookmarks menus. In addition to the page you requested main browser window displays, each display belong to other parts of the user interface.
- Browser engine (browser kernel): The user interface and rendering transfer instructions between engine.
- The rendering engine - responsible for displaying the contents of the request. If the requested content is HTML, it is responsible for parsing HTML and CSS content, and the parsed content displayed on the screen.
- Networking - used for network calls, like HTTP requests. Its interface platform-independent, and provide the underlying implementation for all platforms.
- The rear end user interface - for drawing basic widgets, such as combo boxes and windows. Which discloses a universal interface platform-independent, using the underlying operating system's user interface method.
- JavaScript interpreter. For parsing and executing JavaScript code.
- data storage. This is the persistence layer. The browser needs to save all kinds of data on the hard disk, such as Cookie. The new HTML specification (HTML5) defines 'web database', which is a complete (but lightweight) in the browser database.
We discuss today is the interaction and communication between the browser engine (browser kernel) and JavaScript interpreter (V8 engine).
2: browser engine (browser kernel)
Browser kernel is a multi-threaded processing, which mainly includes the following several threads
- GUI rendering thread: Rendering page
html
elements - JavaScript engine thread: page interaction and
dom
rendering - Timing trigger thread: After a certain time, to trigger a corresponding thread
- Event Trigger thread: When an event triggers the thread, it will put it js event queue waiting to be executed. Commonly used in asynchronous operation.
- Http asynchronous threads: the XMLHttpRequest after the connection is through a browser to open a new thread request, when the state change is detected, if provided with a callback, asynchronous thread state change event generated JavaScript engine into the processing queue waiting to be processed.
There are about links between them:
- JavaScript engine and GUI engine mutually exclusive, while not operate
dom
while rendering pages - JavaScript is single-threaded engine, all need to follow the event processing queue to handle the corresponding code.
- JavaScript engine has a function to listen for events (monitoring process), and will be continuing to check
js
whether the engine main thread execution stack is empty, if empty it will pick up the thread trigger event stored in the event queue callback function to perform.
3: js engine enforcement mechanisms
Because js operating environment is single-threaded, asynchronous operation of some still need to achieve this by means of the browser host. FIG herein to describe a simple js
process when running. The main use of the browser js engine thread and event-triggered thread, sometimes open network services and the timer will be used in other threads.
4: micro task 和 macro task
The event loop browser has to rely on the event queue, but a process than an event queue, can be divided micro task
and macro task
common tasks micro and macro tasks include:
micro task :
- nextTick
- callback
- Promise
- process.nextTick
- Object.observe
- MutationObserver
macro task:
- setTimeout
- setInterval
- I / O
- script代码块
主要部分: 事件队列在同步队列执行完后,首先会执行nextTick,等nextTick执行完成后,然后会先执行micro task, 等micro task队列空了之后,才会去执行macro task,如果中间添加了micro task加入了micro task队列,会继续去执行micro task队列,然后再回到macro task队列。js引擎存在monitoring process进程, 会不停的监听task queue
它们的细节可以参考Tasks和Microtasks
5:event loop和宏任务、微任务
一段代码块就是一个宏任务。所有一般执行代码块的时候,也就是程序执行进入主线程了,主线程会根据不同的代码再分微任务和宏任务等待主线程执行完成后,不停地循环执行。
主线程(宏任务) => 微任务 => 宏任务 => 主线程
下图是一个简易的时间循环:
6: 简易的event loop
console.log('start') Promise.resolve().then((resolve) => { console.log(1) })) console.log('end')
大致流程:
- 这一个代码块相当于一个macro-task,进入主线程。
- 遇到同步的代码
console.log('start')
开始执行 Promise
是micro-task,把它的回调函数放入微任务Event Queue中- 遇到
console.log
开始执行,执行完后,这个代码块宏任务完成,js
监听进程发现主线程空了,就会去寻找微任务。 - 去微任务Event Queue找到Primise的回调函数,执行。
图解浏览器内部执行:
7:复杂的分析
这里我们通过2个复杂的代码来检验是否已经基本了解了事件循环的机制:
// 来至于谷友的一到面试题
<script>
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); </script>
-
整个代码块作为一个宏任务,进入主线程
-
看到函数申明但没有执行,遇到函数console.log执行,输出
script start
-
遇到
setTimeout()
,把它的回调函数放入宏任务(setTimeout1)。宏任务 微任务 setTimeout1 -
遇到执行
async1()
, 进入async
的执行上下文之后,遇到console.log
输出async1 start
-
然后遇到
await async2()
,由于()
的优先级高,所有先执行async2()
,进入async2()
的执行上下文。 -
看到
console.log
输出async2
,之后没有返回值,结束函数,返回undefined
,返回async1
的执行上下文的await undefined
,由于async
函数使用await
后得语句会被放入一个回调函数中,所以把下面的放入微任务中。宏任务 微任务 setTimeout1 async1=> awati 后面的语句 -
结束
async1
,返回全局上下文,遇到Promise
构造函数,里面的函数立马执行, 输出promise1
, 之后的回调函数进入微任务宏任务 微任务 setTimeout1 async1=> awati 后面的语句 new Promise() => 后的then -
执行完Promise(),遇到console.log,输出
script end
,这里一个宏任务代码块执行完毕。 -
在主线程执行的过程中,事件触发线程会一直监听异步事件,当异步事件处理完成后,把它的回调函数放入事件队列,等待执行。
-
主线程现在空闲下来后,执行事件队列中的微任务,然后继续向下执行,遇到
new Promise()
后面的回调函数,执行代码,输出promise2
(这里2个微任务的优先级,promise高于async)。 -
看到
async1
中await
后面的回调函数,执行代码,输出async1 end
宏任务 微任务 setTimeout1 空 -
此时微任务中的队列为空,开始执行队列中的宏任务,进入一个新的代码块。遇到
console.log
,输出setTimeout
-
执行完成,最后结果为
```javascript
script start => async1 start => async2 => promise1 => script end => promise2 => async1 end => setTimeout ```
8:总结
- JavaScript运行环境是单线程,不管什么代码,什么框架的异步代码,都是利用宿主对象(Node,浏览器)的其他线程来通过事件循环机制实现
- 我们一般说的运行和执行是不一样的,JavaScript的运行环境是JavaScript解析引擎,执行环境比如node,浏览器。
- JavaScript解析引擎会监听事件循环机制,把异步任务放入事件队列。
链接参考: