前端面試必備——異步(async)

1340.640.jpg

1.引言

上一篇文章寫了異步中的promise強烈推薦看),這一篇文章準備寫一下異步中的generator和async函數,下一篇文章準備寫js中的運行機制,也就是eventloop和宏任務微任務,這樣異步三兄弟就齊齊整整了。每篇文章後面都會配套相應的大廠面試題,你們必定不要錯過。面試

2. generator

Generator 函數是 ES6 提供的一種異步編程解決方案,語法行爲與傳統函數徹底不一樣。
Generator 函數是一個狀態機,封裝了多個內部狀態。yield至關於隔斷點
執行 Generator 函數會返回一個遍歷器對象,經過調用next方法使狀態不斷改變
next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值。編程

//readFile是我本身封裝的一個讀取文件的方法,返回Promise
function * read() {
    let age = yield readFile('./name.txt','utf8');
    let agedata = yield readFile(`./${age}`,'utf8');
    return agedata
}

上面read是一個generator函數,segmentfault

let it = read()
it是一個遍歷器對象,函數調用可是裏面代碼尚未執行
let {value,done} = it.next()
第一次next函數開始執行知道遇到yield,此時返回一個對象,vlaue是yield後面的這個promise,done是false
value.then((data)=>{let {value,done} = it.next(data)})
等第一個文件讀取完,我再next一下,傳入讀取的結果,這個結果會做爲上一個yield的返回值賦值給a,並執行第二個yield的語句此時返回值agedata爲{讀第二個文件的promise,false}
`value.then((data)=>{promise

console.log(data)   //打印第二個文件的內容
it.next() //函數結束,返回一個{value:第二個文件的內容,done:true}

})`瀏覽器

3. co函數

generator是解決異步的另一個方案,可是咱們須要手動調用next方法,總感受挺麻煩的,tj大神寫了一個co庫,代碼就幾行,能幫咱們自動調用next方法,解決這generator的這個缺陷異步

co(it){
    return new Promise((resolve,reject)=>{
        function next(val){
            {value,done}=it.next(val)  //手動next程序到第一個yield的地方
            if(done){ //都已經成功了還有什麼好說的,直接返回結果就好了
                resolve(value)
            }else{
                return Promise.resolve(value).then(next,rejext)  //核心,看懂co的思想就會了
            }
        }
        next(undefined)
    })
}

重點講解一下第八行,首先強烈建議看個人上篇文章promise
value是第一個yiled的返回值,多是個同步,多是個settimeout,多是個Promise,因此不論是什麼,我先給它包裝成promise,讓他有then方法,而後我在then方法裏面遞歸調用next,若是後面有yield再進next方法裏就會走else自動調next,這樣一個自動執行器就造成了,若是沒有就resolve就好了。須要特別注意的是Promise.resolve(value).then(next)這個寫法,next實際上是有參數的,就是value的異步結果async

咱們再用generator函數就能夠這麼用了異步編程

//read是章節2中的generator函數
co(read()).then((data)=>{
    console.log(data)
})

是否是超級簡單函數

4. async、await

async、awiat實際上就是generator+co函數的語法糖,實際也能夠說就是generator+promise實現的,就是generator+自執行器oop

function * read() {
    let age = yield readFile('./name.txt','utf8');
    let agedata = yield readFile(`./${age}`,'utf8');
    return agedata
}
-------------------------------------------------------
async read(){
    let age = await readFile('./name.txt','utf8');
    let agedata = await readFile(`./${age}`,'utf8');
    return agedata
}

從上面能夠看出是否是就是*換成async,yield換成await?
可是咱們用同步寫法寫async 可使用try catch

5. await時發生了什麼

原本這章我以爲應該放在下篇文章裏面講的,不過,既然剛講了async await的原理,咱就打鐵趁熱吧
若是你有eventloop和微任務宏任務的相關知識,那麼接着往下看,不然請移步到個人下篇文章

5.1 執行順序

先講一下執行順序
promise.then 是一個微任務,因此瀏覽器裏它確定不是settimeout實現的,上一篇文章只是寫了個符合A+規範的實現

async read(){
    console.log(1)  
    let age = await readFile('./name.txt','utf8');
    let agedata = await readFile(`./${age}`,'utf8');
    return agedata
}

read執行時,是否是先回默認調一下next,因此第一個await前的都是同步代碼,而後第一個await後面的是否是會包裝成一個promise而後調用then方法呀?then了之後調用next後面的代碼纔會執行。因此第一個await後面的代碼能夠當作再promise.then裏面的,至關因而一個微任務,因此程序在主棧裏面執行async函數,會先執行await前面的宏任務,而後遇到await,await後面的會先放到微任務隊列裏面,返回主棧去執行主棧裏面的其餘宏任務

比較官方的說法:async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操做完成,再執行函數體內後面的語句。能夠理解爲,是讓出了線程,跳出了 async 函數體。

5.2 await的一些狀況

說一些比較可能的狀況

  • await後面是個基本類型
    包裝成一個promise後是否是能夠直接調用then中next方法?因此基本類型不會影響
  • 若是是一個promise

若是等待一下時間能成功,那麼一段時間會繼續日後執行,緣由和以前的同樣
若是是一個一段時間後失敗的Promise呢,then後面會走reject,而co函數裏面reject中可沒有調用next方法哦因此函數就掛了後面的代碼不會執行,會默認拋錯

可是咱們用同步寫法寫async await的時候是能夠用try catch的

6. 面試題

6.1 異步史

setTimeout、Promise、Async/Await 的區別?
JS 異步解決方案的發展歷程以及優缺點?

答案:
回調函數
缺點:回調地獄 沒有返回值
promise
優勢:解決回調地獄,提供的一些方法仍是挺好用的
缺點:仍是要寫大量回調函數,挺麻煩的;錯誤必須捕獲(不捕獲反應不到外面)
generaotr
缺點:須要寫next,或者說缺一個自執行器
async await
優勢 能夠用try catch 捕獲異常,將異步代碼改爲同步代碼
缺點 若是幾個await沒有依賴性,會有性能問題,這個啥意思呢,
好比讀文件1後根據文件1的內容才能讀文件2 這個是有依賴的吧,我用async挺好的,利用next來控制 第一個文件讀2秒,第二個文件讀2秒共四秒
可是若是文件1文件2沒有關係,我明明能夠同時開啓兩個文件的讀取呀,用await就會阻塞,這也是我剛說的promise有些方法仍是很好用的,好比這裏可使用的promise.all

6.2 原理類

Async/Await 如何經過同步的方式實現異步

答案;解釋下generator+手寫一個co函數

相關文章
相關標籤/搜索