JavaScript的事件循環(huán)\運(yùn)行機(jī)制\eventloop
JavaScript的運(yùn)行機(jī)制是工作中最常碰到的,同時(shí)也是筆試 or 面試中會(huì)被問到的問題,故在本文整理了JavaScript的運(yùn)行機(jī)制,以供參考。
JavaScript執(zhí)行機(jī)制,重點(diǎn)有兩點(diǎn):
1.JavaScript是一門單線程語言。
2.Event Loop(事件循環(huán))是JavaScript的執(zhí)行機(jī)制。
既然說js是單線程,那就是在執(zhí)行代碼的時(shí)候是從上往下執(zhí)行的,先來看一段代碼:
1setTimeout(function(){
2 console.log('定時(shí)器開始')
3});
4
5new Promise(function(resolve){
6 console.log('Promise開始');
7 resolve();
8}).then(function(){
9 console.log('執(zhí)行then函數(shù)')
10});
11
12console.log('代碼執(zhí)行結(jié)束');
輸出結(jié)果:
Promise開始
代碼執(zhí)行結(jié)束
執(zhí)行then函數(shù)
定時(shí)器開始
關(guān)于javascript
javascript是一門單線程語言,在最新的HTML5中提出了Web-Worker, 但javascript是單線程這一核心扔未改變。所以一切javascript版的“多線程”都是用單線程模擬出來的,一切javascript多線程都是紙老虎
JS為什么是單線程的
最初設(shè)計(jì)JS是用來在瀏覽器驗(yàn)證表單操控DOM元素的是一門腳本語言,如果js是多線程的,那么兩個(gè)線程同時(shí)對一個(gè)DOM元素進(jìn)行了相互沖突的操作,那么瀏覽器的解析器是無法執(zhí)行的。
js為什么需要異步
如果js中不存在異步,只能自上而下執(zhí)行,如果上一行解析時(shí)間很長,那么下面的代碼就會(huì)被阻塞。對于用戶而言,阻塞就意味著“卡死”,這樣就導(dǎo)致了很差的用戶體驗(yàn)。比如在進(jìn)行ajax請求的時(shí)候如果沒有返回?cái)?shù)據(jù)后面的代碼就沒辦法執(zhí)行
js單線程又是如何實(shí)現(xiàn)異步的呢
js中的異步以及多線程都可以理解成為一種“假象”,就拿h5的WebWorker來說,子線程有諸多限制,不能控制DOM,不能修改全局對象等等,通常只用來做計(jì)算做數(shù)據(jù)處理。
這些限制并沒有違背我們之前的觀點(diǎn),所以說是“假象”。JS異步的執(zhí)行機(jī)制其實(shí)就是事件循環(huán)(eventloop),理解了eventloop機(jī)制,就理解了js異步的執(zhí)行機(jī)制。
JS的事件循環(huán)(eventloop)是怎么運(yùn)作的
事件循環(huán)、eventloop\運(yùn)行機(jī)制 這三個(gè)術(shù)語其實(shí)說的是同一個(gè)東西
“先執(zhí)行同步操作異步操作排在事件隊(duì)列里”這樣的理解其實(shí)也沒有任何問題但如果深入的話會(huì)引出很多其他概念,比如event table和event queue, 我們來看運(yùn)行過程:
首先判斷JS是同步還是異步,同步就進(jìn)入主線程運(yùn)行,異步就進(jìn)入event table.
異步任務(wù)在event table中注冊事件,當(dāng)滿足觸發(fā)條件后,(觸發(fā)條件可能是延時(shí)也可能是ajax回調(diào)),被推入event queue
同步任務(wù)進(jìn)入主線程后一直執(zhí)行,直到主線程空閑時(shí),才會(huì)去event queue中查看是否有可執(zhí)行的異步任務(wù),如果有就推入主線程中。

那怎么知道主線程執(zhí)行棧為空啊?js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查 主線程 執(zhí)行棧是否為空,一旦為空,就會(huì)去event queue那里檢查是否有等待被調(diào)用的函數(shù)。
1let data = [];
2$.ajax({
3 url:www.javascript.com,
4 data:data,
5 success:() => {
6 console.log('發(fā)送成功!');
7 }
8})
9console.log('代碼執(zhí)行結(jié)束');
ajax進(jìn)入event table,注冊回調(diào)函數(shù)success
執(zhí)行 console.log('代碼執(zhí)行結(jié)束')
ajax事件完成,回調(diào)函數(shù)success進(jìn)入event queue
主線程從event queue讀取回調(diào)函數(shù)success并執(zhí)行
setTimeout
1setTimeout(() => {
2 console.log('2秒到了')
3}, 2000)
setTimeout是異步操作首先進(jìn)入event table, 注冊的事件就是它的回調(diào),觸發(fā)條件就是2秒之后,當(dāng)滿足條件回調(diào)被推入event queue,當(dāng)主線程空閑時(shí)會(huì)去event queue里查看是否有可執(zhí)行的任務(wù)。
1console.log(1) // 同步任務(wù)進(jìn)入主線程
2setTimeout(fun(),0) // 異步任務(wù),被放入event table, 0秒之后被推入event queue里
3console.log(3) // 同步任務(wù)進(jìn)入主線程
1、3是同步任務(wù)馬上會(huì)被執(zhí)行,執(zhí)行完成之后主線程空閑去event queue(事件隊(duì)列)里查看是否有任務(wù)在等待執(zhí)行,這就是為什么setTimeout的延遲事件是0毫秒?yún)s在最后執(zhí)行的原因
但setTimeout延時(shí)的時(shí)間有時(shí)候并不是那么準(zhǔn)確
1setTimeout(() => {
2 console.log('2秒到了')
3}, 2000)
4wait(9999999999)
分析運(yùn)行過程:
console進(jìn)入Event Table并注冊,計(jì)時(shí)開始。
執(zhí)行sleep函數(shù),sleep方法雖然是同步任務(wù)但sleep方法進(jìn)行了大量的邏輯運(yùn)算,耗時(shí)超過了2秒
2秒到了,計(jì)時(shí)事件timeout完成,console進(jìn)入Event queue, 但是sleep還沒執(zhí)行完,主線程還被占用,只能等著。
sleep終于執(zhí)行完了, console終于從event queue進(jìn)入了主線程執(zhí)行,這個(gè)時(shí)候已經(jīng)遠(yuǎn)遠(yuǎn)超過了2秒
其實(shí)延遲2秒只是表示2秒后,setTimeout里的函數(shù)被推入event queue , 而event queue(事件隊(duì)列)里的任務(wù),只有在主線程空閑時(shí)才會(huì)執(zhí)行。
上述流程走完,我們知道setTimeout這個(gè)函數(shù),是經(jīng)過指定時(shí)間后,把要執(zhí)行的任務(wù)(本例子中為console)加入到event queue中, 又因?yàn)閱尉€程任務(wù)要一個(gè)一個(gè)執(zhí)行,如果前面的任務(wù)需要的時(shí)間太久,那么只能等著,導(dǎo)致真正的延遲時(shí)間遠(yuǎn)遠(yuǎn)大于2秒。我們還經(jīng)常遇到setTimeout(fn,0)這樣的代碼,它的含義是,指定某個(gè)任務(wù)在主線最早的空閑時(shí)間執(zhí)行,意思就是不用再等多少秒了, 只要主線程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成,棧為空就馬上執(zhí)行。但是即便主線程為空,0毫秒實(shí)際上也是達(dá)不到的。根據(jù)HTML的標(biāo)準(zhǔn),最低是4毫秒。
setIntval
以setIntval(fn,ms)為例,setIntval是循環(huán)執(zhí)行的,setIntval會(huì)每隔指定的時(shí)間將注冊的函數(shù)置入event queue,不是每過ms會(huì)執(zhí)行一次fn,而是每過ms秒,會(huì)有fn進(jìn)入event queue。需要注意一點(diǎn)的是,一旦setIntval的回調(diào)函數(shù)fn執(zhí)行時(shí)間超過了延遲事件ms,那么就完成看不出來有時(shí)間間隔了。
除了廣義的同步任務(wù)和異步任務(wù),我們對任務(wù)有更精細(xì)的定義:
宏任務(wù) 包含整個(gè)script代碼塊,setTimeout, setIntval
微任務(wù) Promise , process.nextTick
在劃分宏任務(wù)、微任務(wù)的時(shí)候并沒有提到async/ await的本質(zhì)就是Promise
那事件循環(huán)機(jī)制到底是怎么樣的?
不同類型的任務(wù)會(huì)進(jìn)入對應(yīng)的event queue, 比如setTime和setIntval會(huì)進(jìn)入相同(宏任務(wù))的event queue, 而Promise和process.nextTick會(huì)進(jìn)入相同(微任務(wù))的event queue.
Promise與事件循環(huán)
Promise在初始化時(shí),傳入的函數(shù)是同步執(zhí)行的,然后注冊then回調(diào)。注冊完之后,繼續(xù)往下執(zhí)行同步代碼,在這之前,then的回調(diào)不會(huì)執(zhí)行。同步代碼塊執(zhí)行完畢后,才會(huì)在事件循環(huán)中檢測是否有可用的promise回調(diào),如果有,那么執(zhí)行,如果沒有,繼續(xù)下一個(gè)事件循環(huán)。
1. 宏任務(wù),微任務(wù)都是隊(duì)列, 一段代碼執(zhí)行時(shí),會(huì)先執(zhí)行宏任務(wù)中的同步代碼。2. 進(jìn)行第一輪事件循環(huán)的時(shí)候會(huì)把全部的js腳本當(dāng)成一個(gè)宏任務(wù)來運(yùn)行。
3. 如果執(zhí)行中遇到setTimeout之類的宏任務(wù),那么就把這個(gè)setTimeout內(nèi)部的函數(shù)推入[宏任務(wù)的隊(duì)列]中,下一輪宏任務(wù)執(zhí)行時(shí)調(diào)用。
4. 如果執(zhí)行中遇到promise.then()之類的微任務(wù),就會(huì)推入到[當(dāng)前宏任務(wù)的微任務(wù)隊(duì)列]中, 在本輪宏任務(wù)的同步代碼都執(zhí)行完成后,依次執(zhí)行所有的微任務(wù)。
5. 第一輪事件循環(huán)中當(dāng)執(zhí)行完全部的同步腳步以及微任務(wù)隊(duì)列中的事件,這一輪事件循環(huán)就結(jié)束了, 開始第二輪事件循環(huán)。
6. 第二輪事件循環(huán)同理先執(zhí)行同步腳本,遇到其他宏任務(wù)代碼塊繼續(xù)追加到[宏任務(wù)的隊(duì)列]中,遇到微任務(wù),就會(huì)推入到[當(dāng)前宏任務(wù)的微任務(wù)隊(duì)列]中,在本輪宏任務(wù)的同步代碼執(zhí)行都完成后, 依次執(zhí)行當(dāng)前所有的微任務(wù)。
7. 開始第三輪循環(huán)往復(fù)..
下面用代碼來深入理解上面的機(jī)制:
1setTimeout(function() {
2 console.log('4')
3})
4
5new Promise(function(resolve) {
6 console.log('1') // 同步任務(wù)
7 resolve()
8}).then(function() {
9 console.log('3')
10})
11console.log('2')
這段代碼作為宏任務(wù),進(jìn)入主線程。
先遇到setTimeout,那么將其回調(diào)函數(shù)注冊后分發(fā)到宏任務(wù)event queue.
接下來遇到Promise, new Promise立即執(zhí)行,then函數(shù)分發(fā)到微任務(wù)event queue
遇到console.log(), 立即執(zhí)行
整體代碼script作為第一個(gè)宏任務(wù)執(zhí)行結(jié)束, 查看當(dāng)前有沒有可執(zhí)行的微任務(wù),執(zhí)行then的回調(diào)。(第一輪事件循環(huán)結(jié)束了,我們開始第二輪循環(huán))
從宏任務(wù)的event queue開始,我們發(fā)現(xiàn)了宏任務(wù)event queue中setTimeout對應(yīng)的回調(diào)函數(shù),立即執(zhí)行。執(zhí)行結(jié)果:1-2-3-4
整體script作為第一個(gè)宏任務(wù)進(jìn)入主線程,遇到console.log(1)輸出1
遇到setTimeout, 其回調(diào)函數(shù)被分發(fā)到宏任務(wù)event queue中。我們暫且記為setTimeout1
3.遇到process.nextTick(),其回調(diào)函數(shù)被分發(fā)到微任務(wù)event queue中,我們記為process1
4.遇到Promise, new Promise直接執(zhí)行,輸出7.then被分發(fā)到微任務(wù)event queue中,我們記為then1又遇到setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)event queue中,我們記為setTimeout2.
現(xiàn)在開始執(zhí)行微任務(wù), 我們發(fā)現(xiàn)了process1和then1兩個(gè)微任務(wù),執(zhí)行process1,輸出6,執(zhí)行then1,輸出8, 第一輪事件循環(huán)正式結(jié)束, 這一輪的結(jié)果輸出1,7,6,8.那么第二輪事件循環(huán)從setTimeout1宏任務(wù)開始
首先輸出2, 接下來遇到了process.nextTick(),統(tǒng)一被分發(fā)到微任務(wù)event queue,記為process2
8.new Promise立即執(zhí)行,輸出4,then也被分發(fā)到微任務(wù)event queue中,記為then2現(xiàn)在開始執(zhí)行微任務(wù),我們發(fā)現(xiàn)有process2和then2兩個(gè)微任務(wù)可以執(zhí)行輸出3,5. 第二輪事件循環(huán)結(jié)束,第二輪輸出2,4,3,5. 第三輪事件循環(huán)從setTimeout2宏任務(wù)開始
10。直接輸出9,跟第二輪事件循環(huán)類似,輸出9,11,10,12完整輸出是1,7,6,8,2,4,3,5,9,11,10,12(請注意,node環(huán)境下的事件監(jiān)聽依賴libuv與前端環(huán)境不完全相同,輸出順序可能會(huì)有誤差)
1console.log('1')
2setTimeout(function() {
3 console.log('2')
4 process.nextTick(function() {
5 console.log('3')
6 })
7 new Promise(function(resolve) {
8 console.log('4')
9 resolve()
10 }).then(function() {
11 console.log('5')
12 })
13})
14
15process.nextTick(function() {
16 console.log('6')
17})
18
19new Promise(function(resolve) {
20 console.log('7')
21 resolve()
22}).then(function() {
23 console.log('8')
24})
25
26setTimeout(function() {
27 console.log('9')
28 process.nextTick(function() {
29 console.log('10')
30 })
31 new Promise(function(resolve) {
32 console.log('11')
33 resolve()
34 }).then(function() {
35 console.log('12')
36 })
37})
如果是setTimeout里面嵌套setTimeout, 那么嵌套的setTimeout的宏任務(wù)要在外面的宏任務(wù)排序的后面,往后排。看個(gè)例子
1new Promise(function (resolve) {
2 console.log('1')// 宏任務(wù)一
3 resolve()
4}).then(function () {
5 console.log('3') // 宏任務(wù)一的微任務(wù)
6})
7setTimeout(function () { // 宏任務(wù)二
8 console.log('4')
9 setTimeout(function () { // 宏任務(wù)五
10 console.log('7')
11 new Promise(function (resolve) {
12 console.log('8')
13 resolve()
14 }).then(function () {
15 console.log('10')
16 setTimeout(function () { // 宏任務(wù)七
17 console.log('12')
18 })
19 })
20 console.log('9')
21 })
22})
23setTimeout(function () { // 宏任務(wù)三
24 console.log('5')
25})
26setTimeout(function () { // 宏任務(wù)四
27 console.log('6')
28 setTimeout(function () { // 宏任務(wù)六
29 console.log('11')
30 })
31})
32console.log('2') // 宏任務(wù)一
初步總結(jié):宏任務(wù)是一個(gè)棧按先入先執(zhí)行的原則,微任務(wù)也是一個(gè)棧也是先入先執(zhí)行。但是每個(gè)宏任務(wù)都對應(yīng)會(huì)有一個(gè)微任務(wù)棧,宏任務(wù)在執(zhí)行過程中會(huì)先執(zhí)行同步代碼再執(zhí)行微任務(wù)棧。
async/await是什么
我們創(chuàng)建了promise但不能同步等待它執(zhí)行完成。我們只能通過then傳一個(gè)回調(diào)函數(shù)這樣很容易再次陷入promise的回調(diào)地獄。實(shí)際上, async/await在底層轉(zhuǎn)換成了promise和then回調(diào)函數(shù),也就是說, 這是promise的語法糖。每次我們使用await, 解釋器都創(chuàng)建一個(gè)promise對象,然后把剩下的async函數(shù)中的操作放到then回調(diào)函數(shù)中。async/await的實(shí)現(xiàn),離不開promise. 從字面意思來理解, async是“異步”的簡寫,而await是async wait的簡寫可以認(rèn)為是等待異步方法執(zhí)行完成。
async/await用來干什么
用來優(yōu)化promise的回調(diào)問題,被稱為是異步的終極解決方案
async/await內(nèi)部做了什么
async函數(shù)會(huì)返回一個(gè)Promise對象,如果在函數(shù)中return一個(gè)直接量(普通變量),async會(huì)把這個(gè)直接量通過Promise.resolve()封裝成Promise對象。如果你返回了promise那就以你返回的promise為準(zhǔn)。await是在等待,等待運(yùn)行的結(jié)果也就是返回值。await后面通常是一個(gè)異步操作(promise),但是這不代表await后面只能跟異步才做,await后面實(shí)際是可以接普通函數(shù)調(diào)用或者直接量。
async相當(dāng)于 new Promise,await相當(dāng)于then
await的等待機(jī)制
如果await后面跟的不是一個(gè)promise,那await后面表達(dá)式的運(yùn)算結(jié)果就是它等到的東西,如果await后面跟的是一個(gè)promise對象,await它會(huì)'阻塞'后面的diamante,等著promise對象resolve, 然后得到resolve的值作為await表達(dá)式的運(yùn)算結(jié)果。但是此"阻塞"非彼“阻塞”,這就是await必須用在async函數(shù)中的原因。async函數(shù)調(diào)用不會(huì)造成"阻塞",它內(nèi)部所有的“阻塞”都被封裝在一個(gè)promise對象中異步執(zhí)行(這里的阻塞理解成異步等待更合理)
async/await在使用過程中有什么規(guī)定
每個(gè)async方法都返回一個(gè)promise, await只能出現(xiàn)在async函數(shù)中
async/await在什么場景使用
單一的promise鏈并不能發(fā)現(xiàn)async/await的優(yōu)勢,但是如果需要處理由多個(gè)promise組成的then鏈的時(shí)候,優(yōu)勢就能體現(xiàn)出來了(Promise通過then鏈來解決多層回調(diào)的問題,現(xiàn)在又用async/awai來進(jìn)一步優(yōu)化它)
async/await如何使用
假設(shè)一個(gè)業(yè)務(wù),分多個(gè)步驟完成,每個(gè)步驟都是異步的且依賴于上一個(gè)步驟的結(jié)果
1function myPromise(n) {
2 return new Promise(resolve => {
3 console.log(n)
4 setTimeout(() => resolve(n+1), n)
5 })
6}
7function step1(n) {
8 return myPromise(n)
9}
10function step2(n) {
11 return myPromise(n)
12}
13function step3(n) {
14 return myPromise(n)
15}
16
17//如果用 Promise 實(shí)現(xiàn)
18step1(1000)
19.then(a => step2(a))
20.then(b => step3(b))
21.then(result => {
22 console.log(result)
23})
24
25//如果用 async/await 來實(shí)現(xiàn)呢
26async function myResult() {
27 const a = await step1(1000)
28 const b = await step2(a)
29 const result = await step3(b)
30 return result
31}
32myResult().then(result => {
33 console.log(result)
34}).catch(err => {
35 // 如果myResult內(nèi)部有語法錯(cuò)誤會(huì)觸發(fā)catch方法
36})
看的出來async/await的寫法更多優(yōu)雅一些要比promise的鏈接調(diào)用更多直觀也易于維護(hù)
我們來看在任務(wù)隊(duì)列中async/await的運(yùn)行機(jī)制,先給出大概方向再通過案例來證明:
async定義的是一個(gè)promise函數(shù)和普通函數(shù)一樣只要不調(diào)用就不會(huì)進(jìn)入事件隊(duì)列。
async內(nèi)部如果沒有主動(dòng)return promise, 那么async會(huì)把函數(shù)的返回值用promise包裝
await關(guān)鍵字必須出現(xiàn)在async函數(shù)中,await后面不是必須要跟一個(gè)異步操作,也可以是一個(gè)普通表達(dá)式
遇到await關(guān)鍵字,await右邊的語句會(huì)被立即執(zhí)行然后await下面的代碼進(jìn)入等待狀態(tài),等待await得到結(jié)果。await后面如果不是promise對象,await會(huì)阻塞后面的代碼,先執(zhí)行async外面的同步代碼,同步代碼執(zhí)行完,再回到async內(nèi)部,把這個(gè)非promise的東西,作為await表達(dá)式的結(jié)果。await后面如果是promise對象,await也會(huì)暫停async后面的代碼,先執(zhí)行async外面的同步代碼,等著promise對象fulfilled,然后把resolve的參數(shù)作為await表達(dá)式的運(yùn)算結(jié)果。
1setTimeout(function () {
2 console.log('6')
3}, 0)
4console.log('1')
5async function async1() {
6 console.log('2')
7 await async2()
8 console.log('5')
9}
10async function async2() {
11 console.log('3')
12}
13async1()
14console.log('4')
6是宏任務(wù)在下一輪事件循環(huán)執(zhí)行
先同步輸出1,然后調(diào)用async1(),輸出2
await async2()會(huì)先運(yùn)行async2(), 5進(jìn)入等待狀態(tài)
輸出3, 這個(gè)時(shí)候先執(zhí)行async函數(shù)外的同步代碼輸出4.
最后await拿到等待的結(jié)果繼續(xù)往下執(zhí)行輸出5.
6, 進(jìn)入第二輪事件循環(huán)輸出6.
測試代碼的輸出結(jié)果,看到async1函數(shù)輸出2, 立馬執(zhí)行await 的async2函數(shù),輸出3, 但是沒有立即返回,而是先執(zhí)行async2外面的同步代碼,最后得到返回
值111111給await async2函數(shù),

1console.log('1')
2async function async1() {
3 console.log('2')
4 await 'await的結(jié)果'
5 console.log('5')
6}
7
8async1()
9console.log('3')
10
11new Promise(function (resolve) {
12 console.log('4')
13 resolve()
14}).then(function () {
15 console.log('6')
16})
首先輸出1, 然后進(jìn)入async1函數(shù),輸出2
await后面雖然是一個(gè)直接量,但是還是先執(zhí)行async函數(shù)外的同步代碼
輸出3, 進(jìn)入promise輸出4,then回調(diào)進(jìn)入微任務(wù)隊(duì)列
現(xiàn)在同步代碼執(zhí)行完了, 回到async函數(shù)繼續(xù)執(zhí)行輸出5
5, 最后運(yùn)行微任務(wù)輸出6
1async function async1() {
2 console.log('2')
3 await async2()
4 console.log('7')
5}
6
7async function async2() {
8 console.log('3')
9}
10
11setTimeout(function () {
12 console.log('8')
13}, 0)
14
15console.log('1')
16async1()
17
18new Promise(function (resolve) {
19 console.log('4')
20 resolve()
21}).then(function () {
22 console.log('6')
23})
24console.log('5')
首先輸出同步代碼1, 然后進(jìn)入async1方法輸出2
因?yàn)橛龅絘wait所以先進(jìn)群async2方法, 后面的7處于等待狀態(tài)
在async2中輸出3, 現(xiàn)在跳出async函數(shù)先執(zhí)行外面的同步代碼
輸出4,5.then回調(diào)進(jìn)入微任務(wù)棧
現(xiàn)在宏任務(wù)執(zhí)行完了,然后回到async1函數(shù)接著往下執(zhí)行輸出7
執(zhí)行微任務(wù)輸出6
進(jìn)入下一輪事件循環(huán)輸出8
完整輸出:1-2-3-4-5-7-6-8
1async function async1() {
2 console.log('2')
3 const data = await async2()
4 console.log(data)
5 console.log('8')
6}
7
8async function async2() {
9 return new Promise(function (resolve) {
10 console.log('3')
11 resolve('await的結(jié)果')
12 }).then(function (data) {
13 console.log('6')
14 return data
15 })
16}
17console.log('1')
18
19setTimeout(function () {
20 console.log('9')
21}, 0)
22
23async1()
24
25new Promise(function (resolve) {
26 console.log('4')
27 resolve()
28}).then(function () {
29 console.log('7')
30})
31console.log('5')
函數(shù)async1和async2只是定義先不管它, 首先輸出1
setTimeout作為宏任務(wù)進(jìn)入宏任務(wù)隊(duì)列等待下一輪事件循環(huán)
進(jìn)入async1函數(shù)輸出2,await下面的代碼進(jìn)入等待狀態(tài)。
進(jìn)入async2函數(shù)輸出3,then回調(diào)進(jìn)入微任務(wù)隊(duì)列
現(xiàn)在執(zhí)行外面的同步代碼, 輸出4,5,then回調(diào)進(jìn)入微任務(wù)隊(duì)列
6, 按序執(zhí)行微任務(wù),輸出6,7. 現(xiàn)在回到async1函數(shù),
7, 輸出data, 也就是await關(guān)鍵字等到的內(nèi)容, 接著輸出8
8, 進(jìn)行下一輪事件循環(huán)輸出9
執(zhí)行結(jié)果:1-2-3-4-5-6-7-await的結(jié)果-8-9
1setTimeout(function () {
2 console.log('8')
3}, 0)
4
5async function async1() {
6 console.log('1')
7 const data = await async2()
8 console.log('6')
9 return data
10}
11
12async function async2() {
13 return new Promise(resolve => {
14 console.log('2')
15 resolve('async2的結(jié)果')
16 }).then(data => {
17 console.log('4')
18 return data
19 })
20}
21
22async1().then(data => {
23 console.log('7')
24 console.log(data)
25})
26
27new Promise(function (resolve) {
28 console.log('3')
29 resolve()
30}).then(function () {
31 console.log('5')
32})
setTimeout作為宏任務(wù)進(jìn)入宏任務(wù)隊(duì)列等待下一輪事件循環(huán)
先執(zhí)行async1函數(shù), 輸出1,然后6進(jìn)入等待狀態(tài),現(xiàn)在執(zhí)行async2
輸出2, then回調(diào)進(jìn)入微任務(wù)隊(duì)列
4, 接下來執(zhí)行外面的同步代碼3, then回調(diào)進(jìn)入微任務(wù)隊(duì)列
5, 按序列執(zhí)行微任務(wù),輸出4,5. 下面會(huì)帶async1函數(shù)
6,輸出了4之后執(zhí)行return data, await拿到了內(nèi)容
7, 繼續(xù)執(zhí)行輸出6, 執(zhí)行了后面的return data才出發(fā)async1的then回調(diào)輸出7以及data
8, 進(jìn)行第二輪事件循環(huán)輸出8,
完整輸出結(jié)果:1-2-3-4-5-6-7-async2的結(jié)果-8
面試題題目一:
1setTimeout(function() {
2 console.log('4')
3})
4
5new Promise(function(resolve) {
6 console.log('1') // 同步任務(wù)
7 resolve()
8}).then(function() {
9 console.log('3')
10})
11console.log('2')
輸出結(jié)果:1-2-3-4
1console.log('1')
2setTimeout(function() {
3 console.log('2')
4 process.nextTick(function() {
5 console.log('3')
6 })
7 new Promise(function(resolve) {
8 console.log('4')
9 resolve()
10 }).then(function() {
11 console.log('5')
12 })
13})
14
15process.nextTick(function() {
16 console.log('6')
17})
18
19new Promise(function(resolve) {
20 console.log('7')
21 resolve()
22}).then(function() {
23 console.log('8')
24})
25
26setTimeout(function() {
27 console.log('9')
28 process.nextTick(function() {
29 console.log('10')
30 })
31 new Promise(function(resolve) {
32 console.log('11')
33 resolve()
34 }).then(function() {
35 console.log('12')
36 })
輸出結(jié)果:
1-7-6-8-2-4-3-5-9-11-10-12
1setTimeout(function () {
2 console.log('6')
3}, 0)
4console.log('1')
5async function async1() {
6 console.log('2')
7 await async2()
8 console.log('5')
9}
10async function async2() {
11 console.log('3')
12}
13async1()
14console.log('4')
輸出結(jié)果:
1-2-3-4-5-6
1console.log('1')
2async function async1() {
3 console.log('2')
4 await 'await的結(jié)果'
5 console.log('5')
6}
7
8async1()
9console.log('3')
10
11new Promise(function (resolve) {
12 console.log('4')
13 resolve()
14}).then(function () {
15 console.log('6')
16})
輸出結(jié)果:1-2-3-4-5-6
最后聽一首悅耳的歌放松放松,回憶學(xué)到的東西。
點(diǎn)擊下面
播放音樂
長按二維碼關(guān)注,一起努力。
助力尋人啟事
微信公眾號回復(fù) 加群 一起學(xué)習(xí)。
