今天,帶你們來談談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,僅此而已。微信
咱們會想,爲何明明有Generator函數,還須要async函數異步
async函數對 Generator 函數的改進,體如今如下四點。async
(1)內置執行器。Generator函數的執行必須靠執行器,因此纔有了co模塊,而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前端