由于js是单线程的,事件循环是js的执行机制,也是js实现异步的一种方法。程序员
既然js是单线程,那就像只有一个窗口的银行,客户须要排队一个一个办理业务,同理js任务也要一个一个顺序执行。若是一个任务耗时
过长,那么后一个任务也必须等着。那么问题来了,假如咱们想浏览新闻,可是新闻包含的超清图片加载很慢,难道咱们的网页要一直卡着
直到图片彻底显示出来?所以聪明的程序员将任务分为两类:segmentfault
当咱们打开网站时,网页的渲染过程就是一大堆同步任务,好比页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,
就是异步任务。promise
JavaScript中除了普遍的同步任务和异步任务,咱们对任务有更精细的定义:浏览器
总体代码script
,setTimeout
,setInterval
Promise
,process.nextTick
不一样的类型的任务会进入不一样的Event Queue(事件队列),好比setTimeout、setInterval会进入一个事件队列,而Promise会进入
另外一个事件队列。异步
一次事件循环中有宏任务队列和微任务队列。事件循环的顺序,决定js代码执行的顺序。进入总体代码(宏任务-<script>包裹的代码能够 理解为第一个宏任务),开始第一次循环,接着执行全部的微任务。而后再次从宏任务开始,找到其中一个任务队列的任务执行完毕, 再执行全部的微任务。如:
<script> setTimeout(function() { console.log('setTimeout'); }) new Promise(function(resolve) { console.log('promise'); }).then(function() { console.log('then'); }) console.log('console'); /* ----------------------------分析 start--------------------------------- */ 一、`<script>`中的整段代码做为第一个宏任务,进入主线程。即开启第一次事件循环 二、遇到setTimeout,将其回调函数放入Event table中注册,而后分发到宏任务Event Queue中 三、接下来遇到new Promise、Promise,当即执行;将then函数分发到微任务Event Queue中。输出: promise 四、遇到console.log,当即执行。输出: console 五、总体代码做为第一个宏任务执行结束,此时去微任务队列中查看有哪些微任务,结果发现了then函数,而后将它推入主线程并执行。 输出: then 六、第一轮事件循环结束 开启第二轮事件循环。先从宏任务开始,去宏任务事件队列中查看有哪些宏任务,在宏任务事件队列中找到了setTimeout对应的回调函数, 当即执行之。此时宏任务事件队列中已经没有事件了,而后去微任务事件队列中查看是否有事件,结果没有。此时第二轮事件循环结束; 输出:setTimeout /* ----------------------------分析 end--------------------------------- */ </script>
<script> 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') }) }) </script>
a)、整段<script>代码做为第一个宏任务进入主线程,即开启第一轮事件循环 b)、遇到console.log,当即执行。输出:1 c)、遇到setTimeout,将其回调函数放入Event table中注册,而后分发到宏任务事件队列中。咱们将其标记为setTimeout1 d)、遇到process.nextTick,其回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为process1 e)、遇到new Promise、Promise,当即执行;then回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为then1。 输出: 7 f)、遇到setTimeout,将其回调函数放入Event table中注册,而后分发到宏任务事件队列中。咱们将其标记为setTimeout2
此时第一轮事件循环宏任务结束,下表是第一轮事件循环宏任务结束时各Event Queue的状况函数
- | 宏任务事件队列 | 微任务事件队列 |
---|---|---|
第一轮事件循环 | (宏任务已结束) | process一、then1 |
第二轮事件循环(未开始) | setTimeout1 | |
第三轮事件循环(未开始) | setTimeout2 |
能够看到第一轮事件循环宏任务结束后微任务事件队列中还有两个事件待执行,所以这两个事件会被推入主线程,而后执行post
g)、执行process1。输出:6 h)、执行then1。输出:8
第一轮事件循环正式结束!网站
a)、第二轮事件循环从宏任务setTimeout1开始。遇到console.log,当即执行。输出: 2 b)、遇到process.nextTick,其回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为process2 c)、遇到new Promise,当即执行;then回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为then2。输出: 4
此时第二轮事件循环宏任务结束,下表是第二轮事件循环宏任务结束时各Event Queue的状况线程
- | 宏任务事件队列 | 微任务事件队列 |
---|---|---|
第一轮事件循环(已结束) | ||
第二轮事件循环 | (宏任务已结束) | process二、then2 |
第三轮事件循环(未开始) | setTimeout2 |
能够看到第二轮事件循环宏任务结束后微任务事件队列中还有两个事件待执行,所以这两个事件会被推入主线程,而后执行code
d)、执行process2。输出:3 e)、执行then2。输出:5
第二轮事件循环正式结束!
a)、第三轮事件循环从宏任务setTimeout2开始。遇到console.log,当即执行。输出: 9 d)、遇到process.nextTick,其回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为process3 c)、遇到new Promise,当即执行;then回调函数放入Event table中注册,而后被分发到微任务事件队列中。记为then3。输出: 11
此时第三轮事件循环宏任务结束,下表是第三轮事件循环宏任务结束时各Event Queue的状况
- | 宏任务事件队列 | 微任务事件队列 |
---|---|---|
第一轮事件循环(已结束) | ||
第二轮事件循环(已结束) | ||
第三轮事件循环(未开始) | (宏任务已结束) | process三、then3 |
能够看到第二轮事件循环宏任务结束后微任务事件队列中还有两个事件待执行,所以这两个事件会被推入主线程,而后执行
d)、执行process3。输出:10 e)、执行then3。输出:12
1)、js执行从最早进入任务队列的宏任务开始,一般是总体<scirpt>代码
2)、宏任务队列事件所有执行完毕后,检查微任务队列是否有事件,有则执行,直到没有事件为止
3)、更新render(每一次事件循环,浏览器均可能会去更新渲染)
4)、重复以上步骤
https://juejin.im/post/59e85e...
https://segmentfault.com/a/11...