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
I/O
✅
✅
setTimeout
✅
✅
setInterval
✅
✅
setImmediate
❌
✅
requestAnimationFrame
✅
❌
#
浏览器
Node
process.nextTick
❌
✅
MutationObserver
✅
❌
Promise.then catch finally
✅
✅
答案是[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下面的为微任务
6是宏任务在下一轮事件循环执行
先同步输出1,然后调用了async1(),输出2。
await async2() 会先运行async2(),5进入等待状态。
输出3,这个时候先执行async函数外的同步代码输出4。
最后await拿到等待的结果继续往下执行输出5。
进入第二轮事件循环输出6。
resolve
或reject
并不会终结 Promise 的参数函数的执行。一般来说,调用resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
假设有这样的一些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
所执行。