ECMAScript 6-async函式
含義
async 函式是什麼?一句話,它就是 Generator 函式的語法糖
//依次讀取兩個檔案
var asyncReadFile = async function () {
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//一比較就會發現,async函式就是將 Generator 函式的星號(*)替換成async,將yield替換成await,僅此而已
async
(1)內建執行器:Generator 函式的執行必須靠執行器,所以才有了co
模組,而async
函式自帶執行器。
var result = asyncReadFile()
//呼叫了asyncReadFile函式,然後它就會自動執行,輸出最後結果。這完全不像 Generator 函式,需要呼叫next方法,或者用co模組,才能真正執行,得到最後結果。
(2)更廣的適用性:co
模組約定,yield
命令後面只能是 Thunk 函式或 Promise 物件,而async
函式的await
命令後面,可以是Promise 物件和原始型別的值(數值、字串和布林值,但這時等同於同步操作)
(3)返回值是 Promise:async
函式的返回值是 Promise 物件,這比 Generator 函式的返回值是 Iterator 物件方便多了。你可以用then
方法指定下一步的操作
基本用法
async
函式返回一個 Promise 物件,可以使用then
方法添加回調函式。當函式執行的時候,一旦遇到await
就會先返回,等到非同步操作完成,再接著執行函式體內後面的語句
async function getStockPriceByName(name) { var symbol = await getStockSymbol(name);//依次自動執行,返回一個promise物件 var stockPrice = await getStockPrice(symbol); return stockPrice;//return返回的值會成為then方法的回撥引數 } getStockPriceByName('goog').then(function (result) {//result上面返回的值 console.log(result); });
async 函式有多種使用形式
// 函式宣告
async function foo() {}
// 函式表示式
const foo = async function () {};
// 物件的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭頭函式
const foo = async () => {};
語法
-
async函式返回一個 Promise 物件。async函式內部return語句返回的值,會成為then方法回撥函式的引數
async function f() { return 'hello world'; } f().then(v => console.log(v)) //返回值“hello world”被當做引數 // "hello world"
-
async函式內部丟擲錯誤,會導致返回的 Promise 物件變為reject狀態。丟擲的錯誤物件會被catch方法回撥函式接收到
async function f() { throw new Error('出錯了'); } f().then( v => console.log(v),//resoleved狀態時的回撥 e => console.log(e)//reject狀態時的回撥 這裡呼叫這個 ) // Error: 出錯了
-
Promise 物件的狀態變化
只有
async
函式內部的非同步操作執行完,(或者遇到return
語句或者丟擲錯誤,才會執行then
方法指定的回撥函式 -
await 命令
正常情況下,
await
命令後面是一個 Promise 物件。如果不是,會被轉成一個立即resolve
的 Promise 物件async function f() { return await 123;//await後時數值123,它被轉成 Promise 物件,並立即resolve } f().then(v => console.log(v)) // 123
await
命令後面的 Promise 物件如果變為reject
狀態,則reject
的引數會被catch
方法的回撥函式接收到。只要一個await
語句後面的 Promise 變為reject
,那麼整個async
函式都會中斷執行async function f() { await Promise.reject('出錯了'); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // 出錯了 //注意,這裡await語句前面沒有return,但是reject方法的引數依然傳入了catch方法的回撥函式。加上return效果也一樣
如果希望即使前一個非同步操作失敗,也不要中斷後面的非同步操作。這時可以將第一個
await
放在try...catch
結構裡面async function f() { try { await Promise.reject('出錯了'); } catch(e) {} return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello world
另一種方法是
await
後面的 Promise 物件再跟一個catch
方法,處理前面可能出現的錯誤async function f() { await Promise.reject('出錯了') .catch(e => console.log(e)); return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // 出錯了 // hello world
-
錯誤處理
如果
await
後面的非同步操作出錯,那麼等同於async
函式返回的 Promise 物件被reject
async function f() { await new Promise(function (resolve, reject) { throw new Error('出錯了'); }); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // Error:出錯了
防止出錯的方法(防止狀態變為reject),就是將其放在
try...catch
程式碼塊之中async function f() { try { await new Promise(function (resolve, reject) { throw new Error('出錯了'); }); } catch(e) { } return await('hello world'); } //如果有多個await命令,可以統一放在try...catch結構中 async function main() { try { var val1 = await firstStep(); var val2 = await secondStep(val1); var val3 = await thirdStep(val1, val2); console.log('Final: ', val3); } catch (err) { console.error(err); } }
注意
-
await
命令後面的Promise
物件,執行結果可能是rejected
,所以最好把await
命令放在try...catch
程式碼塊中 -
多個
await
命令後面的非同步操作,如果不存在繼發關係,最好讓它們同時觸發。//原始是繼發關係,耗時==>由於這兩個都是獨立的非同步操作,最好寫成同步觸發 let foo = await getFoo(); let bar = await getBar(); // 寫法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 寫法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
-
await
命令只能用在async
函式之中,如果用在普通函式,就會報錯
async 函式的實現原理就是將 Generator 函式和自動執行器,包裝在一個函式裡。