Event-loop
执行顺序
一个掘金的老哥(ssssyoki)的文章摘要: 那么如此看来我给的答案还是对的。但是js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入eventqueue,然后在执行微任务,将微任务放入eventqueue最骚的是,这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回掉函数,然后再从宏任务的queue上拿宏任务的回掉函数。 我当时看到这我就服了还有这种骚操作。
而宏任务一般是:包括整体代码script,setTimeout,setInterval、setImmediate、requestAnimationFrame。
微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、Object.observe(已废弃)、 MutationObserver 记住就行了。
setTimeout和setInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout指定的代码,必须等到本次执行的所有代码都执行完,才会执行。
每一轮Event Loop时,都会将“任务队列”中需要执行的任务,一次执行完。setTimeout和setInterval都是把任务添加到“任务队列”的尾部。因此,它们实际上要等到当前脚本的所有同步任务执行完,然后再等到本次Event Loop的“任务队列”的所有任务执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的任务,一定会按照预定时间执行。
宏任务
# | 浏览器 | Node |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ❌ | ✅ |
| ✅ | ❌ |
微任务
# | 浏览器 | Node |
| ❌ | ✅ |
| ✅ | ❌ |
| ✅ | ✅ |
题目
答案是[1,2,6,4,3,5]。这道题目主要考对JS宏任务和微任务的理解程度,JS的事件循环中每个宏任务称为一个Tick(标记),在每个标记的末尾会追加一个微任务队列,一个宏任务执行完后会执行所有的微任务,直到队列清空。上题中我觉得稍微复杂点的在于async1函数,async1函数本身会返回一个Promise,同时await后面紧跟着async2函数返回的Promise,console.log(3)
其实是在async2函数返回的Promise的then语句中执行的,then语句本身也会返回一个Promise然后追加到微任务队列中,所以在微任务队列中console.log(3)
在console.log(4)
后面
await
遇到await会同步执行await后面的函数,挂起await下面的为微任务
6是宏任务在下一轮事件循环执行
先同步输出1,然后调用了async1(),输出2。
await async2() 会先运行async2(),5进入等待状态。
输出3,这个时候先执行async函数外的同步代码输出4。
最后await拿到等待的结果继续往下执行输出5。
进入第二轮事件循环输出6。
promise
Promise新建后会立即执行
调用resolve
或reject
并不会终结 Promise 的参数函数的执行。
resolve
或reject
并不会终结 Promise 的参数函数的执行。一般来说,调用resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
浏览器操作与requestAnimationFrame
假设有这样的一些DOM
结构:
如果点击#inner
,其执行顺序一定是:click
-> promise
-> observer
-> click
-> promise
-> observer
-> animationFrame
-> animationFrame
-> timeout
-> timeout
。
因为一次I/O
创建了一个宏任务,也就是说在这次任务中会去触发handler
。
按照代码中的注释,在同步的代码已经执行完以后,这时就会去查看是否有微任务可以执行,然后发现了Promise
和MutationObserver
两个微任务,遂执行之。
因为click
事件会冒泡,所以对应的这次I/O
会触发两次handler
函数(_一次在inner
、一次在outer
_),所以会优先执行冒泡的事件(_早于其他的宏任务_),也就是说会重复上述的逻辑。
在执行完同步代码与微任务以后,这时继续向后查找有木有宏任务。
需要注意的一点是,因为我们触发了setAttribute
,实际上修改了DOM
的属性,这会导致页面的重绘,而这个set
的操作是同步执行的,也就是说requestAnimationFrame
的回调会早于setTimeout
所执行。
最后更新于