成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

8張圖幫你一步步看清 async/await 和 promise 的執(zhí)行順序

weizx / 3446人閱讀

摘要:第部分畫圖一步步看清宏任務(wù)微任務(wù)的執(zhí)行過程我們以開篇的經(jīng)典面試題為例,分析這個(gè)例子中的宏任務(wù)和微任務(wù)。注意這里只是把推入微任務(wù)隊(duì)列,并沒有執(zhí)行。執(zhí)行結(jié)束,才能繼續(xù)執(zhí)行后面的代碼如圖此時(shí)當(dāng)前宏任務(wù)都執(zhí)行完了,要處理微任務(wù)隊(duì)列里的代碼。

8張圖讓你一步步看清 async/await 和 promise 的執(zhí)行順序

為什么寫這篇文章?

測試一下自己有沒有必要看

需要具備的前置基礎(chǔ)知識(shí)

主要內(nèi)容

對于async await的理解

畫圖一步步看清宏任務(wù)、微任務(wù)的執(zhí)行過程

為什么寫這篇文章?

說實(shí)話,關(guān)于js的異步執(zhí)行順序,宏任務(wù)、微任務(wù)這些,或者async/await這些慨念已經(jīng)有非常多的文章寫了。

但是怎么說呢,簡單來說,業(yè)務(wù)中很少用async,不太懂a(chǎn)sync呢,

研究了一天,感覺懂了,所手癢想寫一篇 ,哈哈

畢竟自己學(xué)會(huì)的知識(shí),如果連表達(dá)清楚都做不到,怎么能指望自己用好它呢?

測試一下自己有沒有必要看

所以我寫這個(gè)的文章,主要還是交流學(xué)習(xí),如果您已經(jīng)清楚了eventloop/async/await/promise這些東西呢,可以 break 啦

有說的不對的地方,歡迎留言討論,

那么還是先通過一道題自我檢測一下,是否有必要繼續(xù)看下去把。

其實(shí)呢,這是去年一道爛大街的「今日頭條」的面試題 。

我覺得這道題的關(guān)鍵,不僅是說出正確的打印順序,更重要的能否說清楚每一個(gè)步驟,為什么這樣執(zhí)行。

    async function async1() {
        console.log( "async1 start" )
        await async2()
        console.log( "async1 end" )
    }
    
    async function async2() {
        console.log( "async2" )
    }
    
    console.log( "script start" )
    
    setTimeout( function () {
        console.log( "setTimeout" )
    }, 0 )
    
    async1();
    
    new Promise( function ( resolve ) {
        console.log( "promise1" )
        resolve();
    } ).then( function () {
        console.log( "promise2" )
    } )
    
    console.log( "script end" )
注:因?yàn)槭且坏狼岸嗣嬖囶},所以答案是以瀏覽器的eventloop機(jī)制為準(zhǔn)的,在node平臺(tái)上運(yùn)行會(huì)有差異。
     script start
     async1 start
     async2
     promise1
     script end
     promise2
     async1 end
     setTimeout

如果你發(fā)現(xiàn)運(yùn)行結(jié)果跟自己想的一樣,可以選擇跳過這篇文章啦,

或者如果你有興趣看看俺倆的理解有沒有區(qū)別,可以跳到后面的 「畫圖講解的部分」

需要具備的前置知識(shí)

promise的使用經(jīng)驗(yàn)

瀏覽器端的eventloop

不過如果是對 ES7 的 async 不太熟悉,是沒關(guān)系的哈,因?yàn)檫@篇文章會(huì)詳解 async。

那么如果不具備這些知識(shí)呢,推薦幾篇我覺得講得比較清楚的文章

https://segmentfault.com/a/11... 這是我之前寫的講解eventloop的文章,我覺得還算清晰,但是沒涉及 async

https://segmentfault.com/a/11... 這是我讀過的講async await最清楚的文章

http://es6.ruanyifeng.com/#do... promise就推薦阮一峰老師的ES6吧,不過不熟悉 promise 的應(yīng)該較少啦。

主要內(nèi)容 第1部分:對于async await的理解

我推薦的那篇文章,對 async/await 講得更詳細(xì)。不過我希望自己能更加精煉的幫你理解它們

這部分,主要會(huì)講解 3 點(diǎn)內(nèi)容

1.async 做一件什么事情?

2.await 在等什么?

3.await 等到之后,做了一件什么事情?

4.補(bǔ)充: async/await 比 promise有哪些優(yōu)勢?(回頭補(bǔ)充)

1.async 做一件什么事情?

一句話概括: 帶 async 關(guān)鍵字的函數(shù),它使得你的函數(shù)的返回值必定是 promise 對象

也就是

如果async關(guān)鍵字函數(shù)返回的不是promise,會(huì)自動(dòng)用Promise.resolve()包裝

如果async關(guān)鍵字函數(shù)顯式地返回promise,那就以你返回的promise為準(zhǔn)

這是一個(gè)簡單的例子,可以看到 async 關(guān)鍵字函數(shù)和普通函數(shù)的返回值的區(qū)別

async function fn1(){
    return 123
}

function fn2(){
    return 123
}

console.log(fn1())
console.log(fn2())
Promise?{: 123}

123

所以你看,async 函數(shù)也沒啥了不起的,以后看到帶有 async 關(guān)鍵字的函數(shù)也不用慌張,你就想它無非就是把return值包裝了一下,其他就跟普通函數(shù)一樣。

關(guān)于async關(guān)鍵字還有那些要注意的?

在語義上要理解,async表示函數(shù)內(nèi)部有異步操作

另外注意,一般 await 關(guān)鍵字要在 async 關(guān)鍵字函數(shù)的內(nèi)部,await 寫在外面會(huì)報(bào)錯(cuò)。

2.await 在等什么?

一句話概括: await等的是右側(cè)「表達(dá)式」的結(jié)果

也就是說,

右側(cè)如果是函數(shù),那么函數(shù)的return值就是「表達(dá)式的結(jié)果」

右側(cè)如果是一個(gè) "hello" 或者什么值,那表達(dá)式的結(jié)果就是 "hello"

async function async1() {
    console.log( "async1 start" )
    await async2()
    console.log( "async1 end" )
}
async function async2() {
    console.log( "async2" )
}
async1()
console.log( "script start" )

這里注意一點(diǎn),可能大家都知道await會(huì)讓出線程,阻塞后面的代碼,那么上面例子中, "async2" 和 "script start" 誰先打印呢?

是從左向右執(zhí)行,一旦碰到await直接跳出, 阻塞async2()的執(zhí)行?

還是從右向左,先執(zhí)行async2后,發(fā)現(xiàn)有await關(guān)鍵字,于是讓出線程,阻塞代碼呢?

實(shí)踐的結(jié)論是,從右向左的。先打印async2,后打印的script start

之所以提一嘴,是因?yàn)槲医?jīng)常看到這樣的說法,「一旦遇到await就立刻讓出線程,阻塞后面的代碼」

這樣的說法,會(huì)讓我誤以為,await后面那個(gè)函數(shù), async2()也直接被阻塞呢。

3.await 等到之后,做了一件什么事情?

那么右側(cè)表達(dá)式的結(jié)果,就是await要等的東西。

等到之后,對于await來說,分2個(gè)情況

不是promise對象

是promise對象

如果不是 promise , await會(huì)阻塞后面的代碼,先執(zhí)行async外面的同步代碼,同步代碼執(zhí)行完,再回到async內(nèi)部,把這個(gè)非promise的東西,作為 await表達(dá)式的結(jié)果

如果它等到的是一個(gè) promise 對象,await 也會(huì)暫停async后面的代碼,先執(zhí)行async外面的同步代碼,等著 Promise 對象 fulfilled,然后把 resolve 的參數(shù)作為 await 表達(dá)式的運(yùn)算結(jié)果。

第2部分:畫圖一步步看清宏任務(wù)、微任務(wù)的執(zhí)行過程

我們以開篇的經(jīng)典面試題為例,分析這個(gè)例子中的宏任務(wù)和微任務(wù)。

        async function async1() {
            console.log( "async1 start" )
            await async2()
            console.log( "async1 end" )
        }
        async function async2() {
            console.log( "async2" )
        }
        console.log( "script start" )
        setTimeout( function () {
            console.log( "setTimeout" )
        }, 0 )
        async1();
        new Promise( function ( resolve ) {
            console.log( "promise1" )
            resolve();
        } ).then( function () {
            console.log( "promise2" )
        } )
        console.log( "script end" )

先分享一個(gè)我個(gè)人理解的宏任務(wù)和微任務(wù)的慨念,在我腦海中宏任務(wù)和為微任務(wù)如圖所示

也就是「宏任務(wù)」、「微任務(wù)」都是隊(duì)列。

一段代碼執(zhí)行時(shí),會(huì)先執(zhí)行宏任務(wù)中的同步代碼,

如果執(zhí)行中遇到setTimeout之類宏任務(wù),那么就把這個(gè)setTimeout內(nèi)部的函數(shù)推入「宏任務(wù)的隊(duì)列」中,下一輪宏任務(wù)執(zhí)行時(shí)調(diào)用。

如果執(zhí)行中遇到promise.then()之類的微任務(wù),就會(huì)推入到「當(dāng)前宏任務(wù)的微任務(wù)隊(duì)列」中,在本輪宏任務(wù)的同步代碼執(zhí)行都完成后,依次執(zhí)行所有的微任務(wù)1、2、3

下面就以面試題為例子,分析這段代碼的執(zhí)行順序.

每次宏任務(wù)和微任務(wù)發(fā)生變化,我都會(huì)畫一個(gè)圖來表示他們的變化。

直接打印同步代碼 console.log("script start")
首先是2個(gè)函數(shù)聲明,雖然有async關(guān)鍵字,但不是調(diào)用我們就不看。然后首先是打印同步代碼 console.log("script start")

將setTimeout放入宏任務(wù)隊(duì)列
默認(rèn)所包裹的代碼,其實(shí)可以理解為是第一個(gè)宏任務(wù),所以這里是宏任務(wù)2

調(diào)用async1,打印 同步代碼 console.log( "async1 start" )
我們說過看到帶有async關(guān)鍵字的函數(shù),不用害怕,它的僅僅是把return值包裝成了promise,其他并沒有什么不同的地方。所以就很普通的打印 console.log( "async1 start" )

分析一下 await async2()
前文提過await,1.它先計(jì)算出右側(cè)的結(jié)果,2.然后看到await后,中斷async函數(shù)

- 先得到await右側(cè)表達(dá)式的結(jié)果。執(zhí)行async2(),打印同步代碼console.log("async2"), 并且return Promise.resolve(undefined)
- await后,中斷async函數(shù),先執(zhí)行async外的同步代碼

目前就直接打印 console.log("async2")

被阻塞后,要執(zhí)行async之外的代碼

執(zhí)行new Promise(),Promise構(gòu)造函數(shù)是直接調(diào)用的同步代碼,所以 console.log( "promise1" )

代碼運(yùn)行到promise.then()
代碼運(yùn)行到promise.then(),發(fā)現(xiàn)這個(gè)是微任務(wù),所以暫時(shí)不打印,只是推入當(dāng)前宏任務(wù)的微任務(wù)隊(duì)列中。

注意:這里只是把promise2推入微任務(wù)隊(duì)列,并沒有執(zhí)行。微任務(wù)會(huì)在當(dāng)前宏任務(wù)的同步代碼執(zhí)行完畢,才會(huì)依次執(zhí)行

打印同步代碼 console.log( "script end" )
沒什么好說的。執(zhí)行完這個(gè)同步代碼后,「async外的代碼」終于走了一遍

下面該回到 await 表達(dá)式那里,執(zhí)行await Promise.resolve(undefined)了

回到async內(nèi)部,執(zhí)行await Promise.resolve(undefined)

這部分可能不太好理解,我盡量表達(dá)我的想法。

對于 await Promise.resolve(undefined) 如何理解呢?

https://developer.mozilla.org...

根據(jù) MDN 原話我們知道

如果一個(gè) Promise 被傳遞給一個(gè) await 操作符,await 將等待 Promise 正常處理完成并返回其處理結(jié)果。

在我們這個(gè)例子中,就是Promise.resolve(undefined)正常處理完成,并返回其處理結(jié)果。那么await async2()就算是執(zhí)行結(jié)束了。

目前這個(gè)promise的狀態(tài)是fulfilled,等其處理結(jié)果返回就可以執(zhí)行await下面的代碼了。

那何時(shí)能拿到處理結(jié)果呢?

回憶平時(shí)我們用promise,調(diào)用resolve后,何時(shí)能拿到處理結(jié)果?是不是需要在then的第一個(gè)參數(shù)里,才能拿到結(jié)果。

(調(diào)用resolve時(shí),會(huì)把then的參數(shù)推入微任務(wù)隊(duì)列,等主線程空閑時(shí),再調(diào)用它)

所以這里的 await Promise.resolve() 就類似于

Promise.resolve(undefined).then((undefined) => {

})

把then的第一個(gè)回調(diào)參數(shù) (undefined) => {} 推入微任務(wù)隊(duì)列。

then執(zhí)行完,才是await async2()執(zhí)行結(jié)束。

await async2()執(zhí)行結(jié)束,才能繼續(xù)執(zhí)行后面的代碼

如圖

此時(shí)當(dāng)前宏任務(wù)1都執(zhí)行完了,要處理微任務(wù)隊(duì)列里的代碼。

微任務(wù)隊(duì)列,先進(jìn)先出的原則,

執(zhí)行微任務(wù)1,打印promise2

執(zhí)行微任務(wù)2,沒什么內(nèi)容..

但是微任務(wù)2執(zhí)行后,await async2()語句結(jié)束,后面的代碼不再被阻塞,所以打印

console.log( "async1 end" )

宏任務(wù)1執(zhí)行完成后,執(zhí)行宏任務(wù)2

宏任務(wù)2的執(zhí)行比較簡單,就是打印

console.log("setTimeout")

補(bǔ)充在不同瀏覽器上的測試結(jié)果
谷歌瀏覽器,目前是版本是「版本 71.0.3578.80(正式版本) (64 位)」  Mac操作系統(tǒng)

Safari瀏覽器的測試結(jié)果

火狐瀏覽器的測試結(jié)果

如果不理解可以留言,有錯(cuò)誤的話也歡迎指正。

關(guān)于執(zhí)行順序

評論區(qū)有指出

Chrome72 dev版本的執(zhí)行順序是Promise2后打印,

或者是babel編譯過后的代碼是promise2后打印。

我自己也實(shí)踐了一下babel編譯后的代碼執(zhí)行順序的確是promise2后打印的..

原因是ESMA最新規(guī)范的有修改,然后這一點(diǎn)的詳情,說實(shí)話我目前也不是很清楚,評論區(qū)有給出資料,可供參考討論。

https://github.com/rhinel/blo...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99663.html

相關(guān)文章

  • 「今日頭條」前端面試題思路解析

    摘要:一篇文章和一道面試題最近,有篇名為張圖幫你一步步看清和的執(zhí)行順序的文章引起了我的關(guān)注。作者用一道年今日頭條的前端面試題為引子,分步講解了最終結(jié)果的執(zhí)行原因。從字面意思理解,讓我們等等。當(dāng)前的最新版本,在這里的執(zhí)行順序上,的確存在有問題。 一篇文章和一道面試題 最近,有篇名為 《8張圖幫你一步步看清 async/await 和 promise 的執(zhí)行順序》 的文章引起了我的關(guān)注。 作者用...

    寵來也 評論0 收藏0
  • 令人費(fèi)解 async/await 執(zhí)行順序

    摘要:問題的關(guān)鍵在于其執(zhí)行過程中的微任務(wù)數(shù)量,下文中我們需要用上述代碼中的方式對微任務(wù)的執(zhí)行順序進(jìn)行標(biāo)記,以輔助我們理解這其中的執(zhí)行過程。 原文發(fā)布在掘金社區(qū):https://juejin.im/post/5c3cc981f265da616a47e028 起源 2019年了,相信大家對 Promise 和 async/await 都不再陌生了。 前幾日,我在社區(qū)讀到了一篇關(guān)于 async/...

    WilsonLiu95 評論0 收藏0
  • 長期維護(hù)更新,前端面試題整理

    摘要:網(wǎng)上找到的各種面試題整理,長期更新。大部分答案整理來自網(wǎng)絡(luò),有問題的地方,希望大家能指出,及時(shí)修改技術(shù)更新迭代,也會(huì)及時(shí)更新博客原地址前端前端性能優(yōu)化清理文檔,即超文本標(biāo)記語言,幾乎是所有網(wǎng)站的支柱。在最近更新的中,甚至可以創(chuàng)建圖表。 網(wǎng)上找到的各種面試題整理,長期更新。大部分答案整理來自網(wǎng)絡(luò),有問題的地方,希望大家能指出,及時(shí)修改;技術(shù)更新迭代,也會(huì)及時(shí)更新 博客原地址:https:...

    Xufc 評論0 收藏0
  • 深入前端-JavaScript異步編程

    摘要:缺點(diǎn)無法取消當(dāng)處于狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...

    2json 評論0 收藏0
  • 深入前端-JavaScript異步編程

    摘要:缺點(diǎn)無法取消當(dāng)處于狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...

    raise_yang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<