We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
由于JS是单线程的,为了不因为耗时长的任务而阻塞线程,就需要一种机制来协调和调度各任务。
这个机制,就是所谓的event loop(事件循环)。
在了解event loop之前,让我们首先了解一下,什么是同步,什么是异步。
同步行为对应着按顺序执行的代码,每条代码都会严格按照它们出现的顺序来执行。
当上一条代码执行完毕时,下一条代码就能立即获取对应值的变化。
例如下面这段代码:
let a = 1; a += 1; console.log(a)
就算不打印也知道,会输出2。
这就是同步行为,顺序执行且可预测。
相对于同步行为,异步行为是更像是一个黑盒。
知道它是存在的且会执行的,但不知道它什么时候产生结果。
所以在异步行为有了结果之后,需要通知JS的主线程。这个通知,往往是以回调函数的形式出现。
let a = 1; setTimeout(() => { a += 1; console.log(a) }, 1000)
这段代码与同步行为的代码一样都是对a进行运算操作,但不同的是这次主线程并不知道a的值什么时候会发生改变。
因为a的值改变取决于定时器内部的回调函数什么时候被执行,所以当我们想实时获取a变化后的值时,只能够在这个定时器的回调函数里去获取。
异步行为可以理解为不需要等待的任务,当这个任务有了结果时,会以回调的形式进行通知。
首先要知道的是,event loop是一种机制,用来调度任务的执行。
不同的平台对于event loop的实现并不一致,比如浏览器和node.JS的实现就不一样。
浏览器的event loop基于html5中的规范实现,且不同浏览器实现还不一致。
node.JS的event loop基于libuv来实现。
在了解event loop之前,先认识一下宏任务队列与微任务队列。
一些异步任务在有了结果之后会将回调放入宏任务队列等待执行,我们称这些任务为宏任务(macrotask、tasks)。
宏任务包括:
一些异步任务在有了结果之后会将回调放入微任务队列等待执行,我们称这些任务为微任务(microtask、jobs)。
微任务包括:
首先要明确知道的是,JS中只有一条主线程,一次只能处理一件事。
当一个任务在执行时,其他任务都要等待。
先看下图:
按图描述一下在浏览器中JS代码执行的具体流程:
接下来根据具体代码来进行分析:
console.log(1); setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); }); new Promise((resolve, reject) => { console.log(4) resolve(5) }).then((data) => { console.log(data); }) setTimeout(() => { console.log(6); }) console.log(7);
整体流程:
step1:
console.log(1)
分析:由于是同步语句,立即执行输出1
打印结果: 1
Stack: [全局代码script]
Macrotask Queue: []
Microtask Queue: []
step2:
setTimeout(() => { // 这个回调函数叫做callback1,setTimeout属于macrotask,所以放到macrotask queue中 console.log(2); Promise.resolve().then(() => { console.log(3) }); });
分析:setTimeout是宏任务,所以会交给浏览器指定线程处理,有结果(时间到)后再将回调函数放入宏任务队列中等待执行。
Macrotask Queue: [callback1]
step3:
new Promise((resolve, reject) => { console.log(4) resolve(5) }).then((data) => { // 这个回调函数叫做callback2,promise属于microtask,所以放到microtask queue中 console.log(data); })
分析:由于promise构造函数是立即执行的同步代码,所以会立即输出4。then的指定回调会在promise状态改变后加入微任务队列。
打印结果: 1 4
Microtask Queue: [callback2]
step4:
setTimeout(() => { // 这个回调函数叫做callback3,setTimeout属于macrotask,所以放到macrotask queue中 console.log(6); })
分析:setTimeout属于宏任务,有结果(时间到)后将回调函数放入宏任务队列中等待执行。
Macrotask Queue: [callback1, callback3]
step5:
console.log(7)
分析:同步代码立即执行输出7。这句代码结束后,全局script代码执行完毕,执行栈被清空。
打印结果: 1 4 7
Stack: []
step6:
console.log(data); // 这里data是Promise的解决的值,为5
分析:开始检查微任务队列,如果有微任务,则依次执行,直至将微任务队列清空。发现微任务队列中有微任务callback2,取出放入执行栈中执行,输出对应的值5,执行结束后出栈。
打印结果: 1 4 7 5
Stack Queue: []
step7:
console.log(2); //读取到一个解决状态的promise,将then指定回调加入微任务队列中 Promise.resolve().then(() => { //这里命名为callback4 console.log(3) });
分析:微任务队列被清空,执行栈也为空。开始检查宏任务队列,如果有任务,取出队头任务放入执行栈中执行。发现宏任务队列中有宏任务callback1,取出放入执行栈中执行。执行过程中发现有同步语句立即执行,输出2。又读取到一个解决状态的promise,将then指定回调加入微任务队列中。
打印结果: 1 4 7 5 2
Macrotask Queue: [callback3]
Microtask Queue: [callback4]
step8:
console.log(3)
分析:宏任务执行完毕出栈,检查微任务队列,发现微任务callback4,放入执行栈中开始执行,遇见同步代码,立即输出3。执行完毕微任务出栈,微任务队列清空。
打印结果: 1 4 7 5 2 3
step9:
console.log(6)
分析:发现宏任务队列中有宏任务callback3,取出放入执行栈中执行。执行过程中发现有同步语句立即执行,输出6。至此全部代码执行完毕,执行栈为空,宏任务队列为空,微任务队列为空。
最终打印结果: 1 4 7 5 2 3 6
以上就是浏览器中event loop的整个执行流程,这里概括两个重点:
带你彻底弄懂Event Loop
The text was updated successfully, but these errors were encountered:
No branches or pull requests
由于JS是单线程的,为了不因为耗时长的任务而阻塞线程,就需要一种机制来协调和调度各任务。
这个机制,就是所谓的event loop(事件循环)。
同步与异步
在了解event loop之前,让我们首先了解一下,什么是同步,什么是异步。
同步
同步行为对应着按顺序执行的代码,每条代码都会严格按照它们出现的顺序来执行。
当上一条代码执行完毕时,下一条代码就能立即获取对应值的变化。
例如下面这段代码:
就算不打印也知道,会输出2。
这就是同步行为,顺序执行且可预测。
异步
相对于同步行为,异步行为是更像是一个黑盒。
知道它是存在的且会执行的,但不知道它什么时候产生结果。
所以在异步行为有了结果之后,需要通知JS的主线程。这个通知,往往是以回调函数的形式出现。
例如下面这段代码:
这段代码与同步行为的代码一样都是对a进行运算操作,但不同的是这次主线程并不知道a的值什么时候会发生改变。
因为a的值改变取决于定时器内部的回调函数什么时候被执行,所以当我们想实时获取a变化后的值时,只能够在这个定时器的回调函数里去获取。
异步行为可以理解为不需要等待的任务,当这个任务有了结果时,会以回调的形式进行通知。
event loop
首先要知道的是,event loop是一种机制,用来调度任务的执行。
不同的平台对于event loop的实现并不一致,比如浏览器和node.JS的实现就不一样。
浏览器的event loop基于html5中的规范实现,且不同浏览器实现还不一致。
node.JS的event loop基于libuv来实现。
宏任务队列与微任务队列
在了解event loop之前,先认识一下宏任务队列与微任务队列。
宏任务队列(macrotask queue)
一些异步任务在有了结果之后会将回调放入宏任务队列等待执行,我们称这些任务为宏任务(macrotask、tasks)。
宏任务包括:
微任务队列(microtask queue)
一些异步任务在有了结果之后会将回调放入微任务队列等待执行,我们称这些任务为微任务(microtask、jobs)。
微任务包括:
浏览器中的event loop
首先要明确知道的是,JS中只有一条主线程,一次只能处理一件事。
当一个任务在执行时,其他任务都要等待。
先看下图:
按图描述一下在浏览器中JS代码执行的具体流程:
接下来根据具体代码来进行分析:
整体流程:
step1:
分析:由于是同步语句,立即执行输出1
Stack: [全局代码script]
Macrotask Queue: []
Microtask Queue: []
step2:
分析:setTimeout是宏任务,所以会交给浏览器指定线程处理,有结果(时间到)后再将回调函数放入宏任务队列中等待执行。
Stack: [全局代码script]
Macrotask Queue: [callback1]
Microtask Queue: []
step3:
分析:由于promise构造函数是立即执行的同步代码,所以会立即输出4。then的指定回调会在promise状态改变后加入微任务队列。
Stack: [全局代码script]
Macrotask Queue: [callback1]
Microtask Queue: [callback2]
step4:
分析:setTimeout属于宏任务,有结果(时间到)后将回调函数放入宏任务队列中等待执行。
Stack: [全局代码script]
Macrotask Queue: [callback1, callback3]
Microtask Queue: [callback2]
step5:
分析:同步代码立即执行输出7。这句代码结束后,全局script代码执行完毕,执行栈被清空。
Stack: []
Macrotask Queue: [callback1, callback3]
Microtask Queue: [callback2]
step6:
分析:开始检查微任务队列,如果有微任务,则依次执行,直至将微任务队列清空。发现微任务队列中有微任务callback2,取出放入执行栈中执行,输出对应的值5,执行结束后出栈。
Stack Queue: []
Macrotask Queue: [callback1, callback3]
Microtask Queue: []
step7:
分析:微任务队列被清空,执行栈也为空。开始检查宏任务队列,如果有任务,取出队头任务放入执行栈中执行。发现宏任务队列中有宏任务callback1,取出放入执行栈中执行。执行过程中发现有同步语句立即执行,输出2。又读取到一个解决状态的promise,将then指定回调加入微任务队列中。
Stack Queue: []
Macrotask Queue: [callback3]
Microtask Queue: [callback4]
step8:
分析:宏任务执行完毕出栈,检查微任务队列,发现微任务callback4,放入执行栈中开始执行,遇见同步代码,立即输出3。执行完毕微任务出栈,微任务队列清空。
Stack Queue: []
Macrotask Queue: [callback3]
Microtask Queue: []
step9:
分析:发现宏任务队列中有宏任务callback3,取出放入执行栈中执行。执行过程中发现有同步语句立即执行,输出6。至此全部代码执行完毕,执行栈为空,宏任务队列为空,微任务队列为空。
Stack Queue: []
Macrotask Queue: []
Microtask Queue: []
以上就是浏览器中event loop的整个执行流程,这里概括两个重点:
参考链接
带你彻底弄懂Event Loop
The text was updated successfully, but these errors were encountered: