深入理解 JavaScript 非同步系列(5)—— async await

王福朋發表於2017-03-14

第一部分,ES7 中引入 async-await

原文地址 http://www.cnblogs.com/wangfupeng1988/p/6532734.html 未經作者允許,不得轉載~

前面介紹完了Generator的非同步處理,可以說是跌跌撞撞,經過各種基礎介紹和封裝,好容易出了一個比較簡潔的非同步處理方案,學習成本非常高————這顯然不是我們想要的!

因此,還未釋出的 ES7 就乾脆自己參照Generator封裝了一套非同步處理方案————async-await。說是參照,其實可以理解為是Generator的語法糖!

本節示例程式碼參照這裡

本節內容概述

  • Generatorasync-await的對比
  • 使用async-await的不同和好處
  • 接下來...

Generatorasync-await的對比

先來一段Generator處理非同步的程式碼,前面已經介紹過了,看不明白的再獲取接著看。

co(function* () {
    const r1 = yield readFilePromise('some1.json')
    console.log(r1)  // 列印第 1 個檔案內容
    const r2 = yield readFilePromise('some2.json')
    console.log(r2)  // 列印第 2 個檔案內容
})

再來一段async-await的執行程式碼如下,兩者做一個比較。

const readFilePromise = Q.denodeify(fs.readFile)

// 定義 async 函式
const readFileAsync = async function () {
    const f1 = await readFilePromise('data1.json')
    const f2 = await readFilePromise('data2.json')
    console.log('data1.json', f1.toString())
    console.log('data2.json', f2.toString())

    return 'done' // 先忽略,後面會講到
}
// 執行
const result = readFileAsync()

從上面兩端程式碼比較看來,async function代替了function*await代替了yield,其他的再沒有什麼區別了。哦,還有,使用async-await時候不用再引用co這種第三方庫了,直接執行即可。

使用async-await的不同和好處

第一,await後面不能再跟thunk函式,而必須跟一個Promise物件(因此,Promise才是非同步的終極解決方案和未來)。跟其他型別的資料也OK,但是會直接同步執行,而不是非同步。

第二,執行const result = readFileAsync()返回的是個Promise物件,而且上面程式碼中的return 'done'會直接被下面的then函式接收到

result.then(data => {
    console.log(data)  // done
})

第三,從程式碼的易讀性來將,async-await更加易讀簡介,也更加符合程式碼的語意。而且還不用引用第三方庫,也無需學習Generator那一堆東西,使用成本非常低。

因此,如果 ES7 正式釋出了之後,強烈推薦使用async-await。但是現在尚未正式釋出,從穩定性考慮,還是Generator更好一些。

接下來...

node v7 版本已經開始原生支援async-await了,不過 node 的目前穩定版本還是v6,尚不支援,怎麼辦?———— 當然是萬能的babel!下一節就介紹。

 

第二部分,如何在 nodejs v6.x版本中使用 async-await

本節介紹一下如何使用babel來讓 node v6 版本也能執行async-await

本節內容概述

  • 安裝必要的外掛
  • 建立入口檔案並執行

安裝必要的外掛

執行npm i babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-3 babel-runtime --save安裝一堆需要的外掛。

然後在專案根目錄建立.babelrc檔案,檔案內容編寫為

{
  "presets": ["stage-3", "es2015"],
  "plugins": ["transform-runtime"]
}

建立入口檔案並執行

加入你編寫async-await的程式碼檔案是test.js,那麼你需要建立另一個檔案,例如test-entry.js作為入口檔案。入口檔案內容編寫為

require("babel-core/register");
require("./test.js");

然後直接執行node test-entry.js就可以了

 

第三部分,整體總結

一週左右的業餘時間總結完,寫完,也是累得我夠嗆。不算什麼體力活,但是天天的坐在書桌旁寫這些東西也是很考驗一個人的定力,沒點耐性是肯定不行的 ———— 這算是獲獎感言嗎 ?

本節內容概述

  • 基礎知識不可忽略
  • 非同步操作程式碼的變化
  • 寫在最後

礎知識不可忽略

這裡的基礎知識分為兩部分,都不能忽略,都需要深入研究和思考

  • 什麼是非同步,非同步的實現原理,event-loop,以及和事件繫結的關係。這些在最初介紹時,都講過,不要看完了就忘記了;
  • 無論非同步操作的寫法如何變化,JS 還是單執行緒、非同步執行的語言,callback一直都存在而且發揮作用,這個在此前的章節一直強調;

非同步操作程式碼的變化

最後我們來感受一下,從一開始callback方式到後來的async-await方式,前前後後編寫非同步程式碼的變化。從變化中就可以體會到,確實越來越簡潔,越來越易讀。

callback方式

fs.readFile('some1.json', (err, data) => {
    fs.readFile('some2.json', (err, data) => {
        fs.readFile('some3.json', (err, data) => {
            fs.readFile('some4.json', (err, data) => {

            })
        })
    })
})

Promise方式

readFilePromise('some1.json').then(data => {
    return readFilePromise('some2.json')
}).then(data => {
    return readFilePromise('some3.json')
}).then(data => {
    return readFilePromise('some4.json')
})

Generator方式

co(function* () {
    const r1 = yield readFilePromise('some1.json')
    const r2 = yield readFilePromise('some2.json')
    const r3 = yield readFilePromise('some3.json')
    const r4 = yield readFilePromise('some4.json')
})

async-await方式

const readFileAsync = async function () {
    const f1 = await readFilePromise('data1.json')
    const f2 = await readFilePromise('data2.json')
    const f3 = await readFilePromise('data3.json')
    const f4 = await readFilePromise('data4.json')
}

寫在最後

寫到這裡,也沒啥可寫的了,這裡希望大家多多按照自己的思路來思考問題吧。最後,歡迎掃碼轉賬給我打賞,哈哈!

求打賞

如果你看完了,感覺還不錯,歡迎給我打賞 ———— 以激勵我更多輸出優質內容

最後,github地址是 https://github.com/wangfupeng1988/js-async-tutorial 歡迎 star 和 pr

------

學習作者教程:《前端JS高階面試》《前端JS基礎面試題》《React.js模擬大眾點評webapp》《zepto設計與原始碼分析》《json2.js原始碼解讀

 

相關文章