今天,帶你們來談談ES6中的async函數,咱們在理解一個概念的時候,無外乎這是三個方面html
若是感受文章太長,能夠直接拉到下面,看小結🙄前端
ES7提供了async函數
,使得異步操做變得更加方便。async函數
是什麼?一句話,async函數就是Generator函數
的語法糖。node
咱們來個案例,取讀文件git
Generator函數github
var fs = require('fs');
var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
複製代碼
寫成async函數,就是下面這樣。shell
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,僅此而已。bash
咱們會想,爲何明明有Generator函數,還須要async函數微信
async函數對 Generator 函數的改進,體如今如下四點。異步
(1)內置執行器。Generator函數的執行必須靠執行器,因此纔有了co模塊,而async函數自帶執行器。也就是說,async函數的執行,與普通函數如出一轍,只要一行。async
var result = asyncReadFile();
複製代碼
上面的代碼調用了asyncReadFile函數,而後它就會自動執行,輸出最後結果。這徹底不像Generator函數,須要調用next方法,或者用co模塊,才能獲得真正執行,獲得最後結果。
(2)更好的語義。async和await,比起星號和yield,語義更清楚了。async表示函數裏有異步操做,await表示緊跟在後面的表達式須要等待結果。
(3)更廣的適用性。 co模塊約定,yield命令後面只能是Thunk函數或Promise對象,而async函數的await命令後面,能夠是Promise對象和原始類型的值(數值、字符串和布爾值,但這時等同於同步操做)。
(4)返回值是Promise。async函數的返回值是Promise對象,這比Generator函數的返回值是Iterator對象方便多了。你能夠用then方法指定下一步的操做。
進一步說,async函數徹底能夠看做多個異步操做,包裝成的一個Promise對象,而await命令就是內部then命令的語法糖。
async怎麼使用呢?
一個函數前面加上async,就可讓這個函數數成爲異步函數,跳出本來的執行順序
console.log(1)
async function asyfun () {
console.log(2)
}
asyfun();
console.log(3)
// 打印結果:1,3,2
複製代碼
(1)async函數返回一個Promise對象。
async函數內部return語句返回的值,會成爲then方法回調函數的參數。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
複製代碼
上面代碼中,函數f內部return命令返回的值,會被then方法回調函數接收到。
async函數內部拋出錯誤,會致使返回的Promise對象變爲reject狀態。拋出的錯誤對象會被catch方法回調函數接收到。
async function f() {
throw new Error('出錯了');
}
f().then(
v => console.log(v),
e => console.log(e)
)
// Error: 出錯了
複製代碼
(2)async函數返回的Promise對象,必須等到內部全部await命令的Promise對象執行完,纔會發生狀態改變。也就是說,只有async函數內部的異步操做執行完,纔會執行then方法指定的回調函數。
下面是一個例子。
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"
複製代碼
(3)正常狀況下,await命令後面是一個Promise對象。若是不是,會被轉成一個當即resolve的Promise對象。
async function f() {
return await 123;
}
f().then(v => console.log(v))
// 123
複製代碼
上面代碼中,await命令的參數是數值123,它被轉成Promise對象,並當即resolve。
await命令後面的Promise對象若是變爲reject狀態,則reject的參數會被catch方法的回調函數接收到。
async function f() {
await Promise.reject('出錯了');
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出錯了
複製代碼
注意,上面代碼中,await語句前面沒有return,可是reject方法的參數依然傳入了catch方法的回調函數。這裏若是在await前面加上return,效果是同樣的。
只要一個await語句後面的Promise變爲reject,那麼整個async函數都會中斷執行。
async function f() {
await Promise.reject('出錯了');
await Promise.resolve('hello world'); // 不會執行
}
複製代碼
上面代碼中,第二個await語句是不會執行的,由於第一個await語句狀態變成了reject。
爲了不這個問題,能夠將第一個await放在try...catch結構裏面,這樣第二個await就會執行。
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命令,能夠統一放在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);
}
}
複製代碼
(4)若是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:出錯了
複製代碼
上面代碼中,async函數f執行後,await後面的Promise對象會拋出一個錯誤對象,致使catch方法的回調函數被調用,它的參數就是拋出的錯誤對象。具體的執行機制,能夠參考後文的「async函數的實現」。
防止出錯的方法,也是將其放在try...catch代碼塊之中。
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出錯了');
});
} catch(e) {
}
return await('hello world');
}
複製代碼
本文首發於微信公衆號:node前端