async
其實就是 Generator
的語法糖,看本篇文章以前能夠先看一下上一篇文章 Generator函數。理解 Generator
就容易理解爲何說async
是異步編程的完美解決方案了。前端
async
的筆試題const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error){ return reject(error); } resolve(data); }); }); }; const foo = function* () { const f1 = yield readFile('/src/lib'); const f2 = yield readFile('/src/utils'); console.log(f1.toString()); console.log(f2.toString()); }; 複製代碼
把上面代碼的Generator
函數 foo
能夠寫成 async
函數,就是這樣:git
const asyncReadFile = async function () { const f1 = await readFile('/src/lib'); const f2 = await readFile('/src/utils'); console.log(f1.toString()); console.log(f2.toString()); }; 複製代碼
能夠發現,async
函數就是將Generator
函數的星號(*
)替換成async
,將 yield
替換成 await
,僅此而已。github
async
函數是基於 Generator
的改進,體如今如下4點ajax
Generator
函數的執行必須靠執行器。因此纔有了 Thunk
函數和co
模塊,而 async
函數自帶執行器。async
函數的執行和普通函數同樣。asyncReadFile();
複製代碼
更好的語義。 async
和await
,比起星號和yield
,語義更清楚了。 async
表示函數裏有異步操做,await
表示緊跟在後面的表達式須要等待結果。編程
更廣的適應性。 即便 Generator
函數能夠藉助co
模塊自動執行,可是co
模塊後面只能是Thunk
函數或Promise
對象,而async
函數的await
命令後面,能夠是 Promise對象和原始類型的值(數值、字符串和布爾值,但這是會自動轉成當即 resolved
的 Promise對象
)微信
返回值是 Promise
。 aysnc函數返回值爲 Promise
,這比Generator
函數的返回值是Iterator
對象方便多了。markdown
async
函數徹底能夠看做多個異步操做,包裝成的一個Promise
對象,而await命令就是內部then
命令的語法糖。異步
總之就是
Generator
函數雖然是JS借鑑其餘語言,根據JS自己單線程的特色實現的協程,可是使用起來會麻煩不少,每次都要本身去寫執行器,而async
函數就是爲了解決這些重複的工做而生的。其實async
函數就是將Generaor
函數和自動執行器完美地封裝在了一塊兒。async
就是將Generator函數和自動執行器,包裝在一個函數裏。異步編程
async function fn(args) { // ... } function fn(args) { return spawn(function* () { // ... }) } 複製代碼
全部的 async
函數均可以寫成上面的第二種形式,其中 spawn 函數就是自動執行器。
// 接受 Generator 函數做爲參數,返回一個 Promise 對象 function spawn(genF) { return new Promise(function(resolve, reject) { // 執行genF,獲得一個內部指針對象 const gen = genF(); function step(nextF) { let next; // 進行錯誤處理 try { next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } // 將 next.value 轉成 Promise對象 Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } // 會反覆調用 step step(function() { return gen.next(undefined); }); }); } 複製代碼
能夠看到,實際上是 Generator函數和Promise的組合,實現了一個自動執行器,返回 Promise對象
async
的筆試題sleep
:async
實現Promise.all()
的效果每隔1秒輸出 1, 2, 3, 4, 5
function sleep(interval) { return new Promise(resolve => { setTimeout(resolve, interval); }) } // 用法 async function one2FiveInAsync() { for (let i = 1; i <= 5; i++) { console.log(i); await sleep(1000); } } one2FiveInAsync(); 複製代碼
紅燈2秒,黃燈1秒,綠燈3秒
function sleep(duration) { return new Promise(resolve => { setTimeout(resolve, duration); }) } async function changeColor(color, duration) { console.log('當前顏色', color); await sleep(duration); } async function main() { await changeColor('紅色', 2000); await changeColor('黃色', 1000); await changeColor('綠色', 3000); } main(); 複製代碼
假設 getFoo
和getBar
是兩個用於發起ajax
請求的函數。
// 寫法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 寫法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise; 複製代碼
上面兩種寫法,getFoo 和 getBar 都是同時觸發,這樣就會縮短程序的執行時間。
上面只是簡單示例,思考一下,寫出完整代碼。
async
函數原理就是 Generator
函數 和 自動執行器包裝了一下。Generator
就是能夠暫定執行和在以前停下的位置接着執行。好比發送一個接口請求,發出以後,JS能夠去幹其餘的事兒,接口請求回來以後(數據經過next傳入),會接着繼續執行。可是它不能自動執行,因此須要自動執行器, thunk
函數和co
模塊都是,可是async給咱們封裝得更加完美。文章首發於 個人github博客
最近發起了一個100天前端進階計劃,主要是深挖每一個知識點背後的原理,歡迎關注 微信公衆號「牧碼的星星」,咱們一塊兒學習,打卡100天。