Javascript 事件循环 是指浏览器或Node的一种解决JS单线程运行时不会阻塞的一种机制
简单来说,Event Loop(事件循环)是js的执行机制,当从script标签开始执行就会分 异步任务和同步任务,同步任务会直接进入主线程依次执行,异步任务会分宏任务和微任务,在一次事件循环中,微任务队列会先执行完,再执行宏任务,这样就完成了一个事件循环,然后才执行下一个事件循环。
示例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| console.log(1)
setTimeout(function(){ console.log(2) },0)
console.log(3)
执行结果:
分析: 1.console.log(1)是同步任务,直接打印1; 2.setTimeout是异步任务,且是宏函数,放到宏函数队列中,等待下次Event Loop才会执行; 3.console.log(3)是同步任务,直接打印3; 4.主线程执行完毕,没有微任务,那么执行第二个宏任务setTimeout,打印2; 5.结果:1,3,2
|
示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| setTimeout(function(){ console.log(1) });
new Promise(function(resolve){ console.log(2); for(var i = 0; i < 10000; i++){ i == 9999 && resolve(); } }).then(function(){ console.log(3) });
console.log(4);
执行结果:
分析: 1.setTimeout是异步,且是宏函数,放到宏函数队列中; 2.new Promise是同步任务,直接执行,打印2,并执行for循环; 3.promise.then是微任务,放到微任务队列中; 4.console.log(4)同步任务,直接执行,打印4; 5.此时主线程任务执行完毕,检查微任务队列中,有promise.then,执行微任务,打印3; 6.微任务执行完毕,第一次循环结束;从宏任务队列中取出第一个宏任务到主线程执行,打印1; 7.结果:2,4,3,1
|
示例3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| console.log(1);
setTimeout(function() { console.log(2); }, 0);
Promise.resolve().then(function() { console.log(3); }).then(function() { console.log('4.我是新增的微任务'); });
console.log(5);
执行结果:
分析: 1.console.log(1)是同步任务,直接执行,打印1; 2.setTimeout是异步,且是宏函数,放到宏函数队列中; 3.Promise.resolve().then是微任务,放到微任务队列中; 4.console.log(5)是同步任务,直接执行,打印5; 5.此时主线程任务执行完毕,检查微任务队列中,有Promise.resolve().then,执行微任务,打印3; 6.此时发现第二个.then任务,属于微任务,添加到微任务队列,并执行,打印4.我是新增的微任务; 7.这里强调一下,微任务执行过程中,发现新的微任务,会把这个新的微任务添加到队列中,微任务队列依次执行完毕后,才会执行下一个循环; 8.微任务执行完毕,第一次循环结束;取出宏任务队列中的第一个宏任务setTimeout到主线程执行,打印2; 9.结果:1,5,3,4.我是新增的微任务,2
|
示例4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| function add(x, y) { console.log(1) setTimeout(function() { console.log(2) }, 1000) } add();
setTimeout(function() { console.log(3) })
new Promise(function(resolve) { console.log(4) setTimeout(function() { console.log(5) }, 100) for(var i = 0; i < 100; i++) { i == 99 && resolve() } }).then(function() { setTimeout(function() { console.log(6) }, 0) console.log(7) })
console.log(8)
执行结果
分析: 1.add()是同步任务,直接执行,打印1; 2.add()里面的setTimeout是异步任务且宏函数,记做timer1放到宏函数队列; 3.add()下面的setTimeout是异步任务且宏函数,记做timer2放到宏函数队列; 4.new Promise是同步任务,直接执行,打印4; 5.Promise里面的setTimeout是异步任务且宏函数,记做timer3放到宏函数队列; 6.Promise里面的for循环,同步任务,执行代码; 7.Promise.then是微任务,放到微任务队列; 8.console.log(8)是同步任务,直接执行,打印8; 9.此时主线程任务执行完毕,检查微任务队列中,有Promise.then,执行微任务,发现有setTimeout是异步任务且宏函数,记做timer4放到宏函数队列; 10.微任务队列中的console.log(7)是同步任务,直接执行,打印7; 11.微任务执行完毕,第一次循环结束; 12.检查宏任务Event Table,里面有timer1、timer2、timer3、timer4,四个定时器宏任务,按照定时器延迟时间得到可以执行的顺序,即Event Queue:timer2、timer4、timer3、timer1,取出排在第一个的timer2; 13.取出timer2执行,console.log(3)同步任务,直接执行,打印3; 14.没有微任务,第二次Event Loop结束; 15.取出timer4执行,console.log(6)同步任务,直接执行,打印6; 16.没有微任务,第三次Event Loop结束; 17.取出timer3执行,console.log(5)同步任务,直接执行,打印5; 18.没有微任务,第四次Event Loop结束; 19.取出timer1执行,console.log(2)同步任务,直接执行,打印2; 20.没有微任务,也没有宏任务,第五次Event Loop结束; 21.结果:1,4,8,7,3,6,5,2
|
示例5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| setTimeout(function() { console.log(1); setTimeout(function() { console.log(2); }) }, 0); setTimeout(function() { console.log(3); }, 0);
执行结果
分析: 1.第一个setTimeout是异步任务且宏函数,记做timer1放到宏函数队列; 2.第三个setTimeout是异步任务且宏函数,记做timer2放到宏函数队列; 3.没有微任务,第一次Event Loop结束; 4.取出timer1,console.log(1)同步任务,直接执行,打印1; 5.timer1里面的setTimeout是异步任务且宏函数,记做timer3放到宏函数队列; 6.没有微任务,第二次Event Loop结束; 7.取出timer2,console.log(3)同步任务,直接执行,打印3; 8.没有微任务,第三次Event Loop结束; 9.取出timer3,console.log(2)同步任务,直接执行,打印2; 10.没有微任务,也没有宏任务,第四次Event Loop结束; 11.结果:1,3,2
|
示例6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| console.log('1');
setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) })
process.nextTick(function() { console.log('6'); })
new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') })
setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) })
执行结果
分析: 1. console.log(1)是同步任务,直接执行,打印 1; 2. setTimeout是异步,且是宏函数,放到宏任务队列中,暂且记为setTimeout1; 3. process.nextTick() 是微任务,放到微任务队列中,暂且记为process1; 4. new Promise是同步任务,直接执行,打印 7; 5. Promise.then是微任务,放到微任务队列, 暂且记为 then1; 6. setTimeout是异步,且是宏函数,放到宏任务队列中,暂且记为setTimeout2;(此时第一轮事件循环宏任务执行完成,发现了两个微任务) 7. 执行 process1,打印 6; 8. 执行 then1,打印 8; (此时第一轮事件循环正式结束) 9. console.log('2') 是同步任务,直接执行,打印 2; 10. 遇到 process.nextTick() 是微任务,放到微任务队列中,暂且记为process2; 11. new Promise是同步任务,直接执行,打印 4; 12. Promise.then是微任务,放到微任务队列, 暂且记为 then2;(此时第二轮事件循环宏任务执行完成,发现了两个微任务) 13. 执行 process2,打印 3; 14. 执行 then2,打印 5; (此时第二轮事件循环正式结束) 15. console.log('9') 是同步任务,直接执行,打印 9; 16. 遇到 process.nextTick() 是微任务,放到微任务队列中,暂且记为process3; 17. new Promise是同步任务,直接执行,打印 11; 18. Promise.then是微任务,放到微任务队列, 暂且记为 then3;(此时第三轮事件循环宏任务执行完成,发现了两个微任务) 19. 执行 process3,打印 10; 20. 执行 then3,打印 12;
|
注意: process对象是 Node 的一个全局对象,提供当前 Node 进程的信息。它可以在脚本的任意位置使用,不必通过require命令加载。该对象部署了EventEmitter接口
宏任务(macro-task):整体代码script、setTimeOut、setInterval
微任务(mincro-task):promise.then、process.nextTick(node)
参考文章
js运行机制详解(Event Loop)
这一次,彻底弄懂 JavaScript 执行机制