Event Loop
JS中任务分为同步任务和异步任务
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行(鼠标点击、键盘事件、httpq请求、setTimeOut)
具体执行流程:
- 所有同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
不管是setTimeout/setInterval
和XHR/fetch
代码,在这些代码执行时, 本身是同步任务,而其中的回调函数才是异步任务,才会进入到任务队列中
宏任务、微任务
宏任务
我们可以将每次执行栈执行的代码当做是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行), 每一个宏任务会从头到尾执行完毕,不会执行其他
微任务
微任务仅来自于我们的代码。它们通常是由 promise 创建的:对 .then/catch/finally
处理程序的执行会成为微任务。微任务也被用于 await
的“幕后”,因为它是 promise 处理的另一种形式
-
执行一个
宏任务
(栈中没有就从事件队列
中获取) -
执行过程中如果遇到
微任务
,就将它添加到微任务
的任务队列中 -
宏任务
执行完毕后,立即执行当前微任务队列
中的所有微任务
(依次执行) -
当前
宏任务
执行完毕,开始检查渲染,然后GUI线程
接管渲染 -
渲染完毕后,
JS线程
继续接管,开始下一个宏任务
(从事件队列中获取)