ES7 提出的async
函數,終於讓 JavaScript 對於異步操做有了終極解決方案。No more callback hell。async
函數是 Generator
函數的語法糖。使用 關鍵字 async
來表示,在函數內部使用 await
來表示異步。
想較於 Generator,Async
函數的改進在於下面四點:git
內置執行器。Generator 函數的執行必須依靠執行器,而 Aysnc
函數自帶執行器,調用方式跟普通函數的調用同樣github
更好的語義。async
和 await
相較於 *
和 yield
更加語義化npm
更廣的適用性。co
模塊約定,yield
命令後面只能是 Thunk 函數或 Promise對象。而 async
函數的 await
命令後面則能夠是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操做)json
返回值是 Promise。async
函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator 對象方便,能夠直接使用 then()
方法進行調用api
先定義一個 Fetch 方法用於獲取 github user 的信息:babel
function fetchUser() { return new Promise((resolve, reject) => { fetch('https://api.github.com/users/superman66') .then((data) => { resolve(data.json()); }, (error) => { reject(error); }) }); }
Promise 方式異步
/** * Promise 方式 */ function getUserByPromise() { fetchUser() .then((data) => { console.log(data); }, (error) => { console.log(error); }) } getUserByPromise();
Promise 的方式雖然解決了 callback hell,可是這種方式充滿了 Promise的 then()
方法,若是處理流程複雜的話,整段代碼將充滿 then
。語義化不明顯,代碼流程不能很好的表示執行流程。async
Generator 方式函數
/** * Generator 方式 */ function* fetchUserByGenerator() { const user = yield fetchUser(); return user; } const g = fetchUserByGenerator(); const result = g.next().value; result.then((v) => { console.log(v); }, (error) => { console.log(error); })
Generator 的方式解決了 Promise 的一些問題,流程更加直觀、語義化。可是 Generator 的問題在於,函數的執行須要依靠執行器,每次都須要經過 g.next()
的方式去執行。fetch
async 方式
/** * async 方式 */ async function getUserByAsync(){ let user = await fetchUser(); return user; } getUserByAsync() .then(v => console.log(v));
async
函數完美的解決了上面兩種方式的問題。流程清晰,直觀、語義明顯。操做異步流程就如同操做同步流程。同時 async
函數自帶執行器,執行的時候無需手動加載。
async 函數返回一個 Promise 對象
async
函數內部 return 返回的值。會成爲 then
方法回調函數的參數。
async function f() { return 'hello world' }; f().then( (v) => console.log(v)) // hello world
若是 async
函數內部拋出異常,則會致使返回的 Promise 對象狀態變爲 reject
狀態。拋出的錯誤而會被 catch
方法回調函數接收到。
async function e(){ throw new Error('error'); } e().then(v => console.log(v)) .catch( e => console.log(e));
async 函數返回的 Promise 對象,必須等到內部全部的 await 命令的 Promise 對象執行完,纔會發生狀態改變
也就是說,只有當 async
函數內部的異步操做都執行完,纔會執行 then
方法的回調。
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout)); async function f(){ await delay(1000); await delay(2000); await delay(3000); return 'done'; } f().then(v => console.log(v)); // 等待6s後才輸出 'done'
正常狀況下,await 命令後面跟着的是 Promise ,若是不是的話,也會被轉換成一個 當即 resolve 的 Promise
以下面這個例子:
async function f() { return await 1 }; f().then( (v) => console.log(v)) // 1
catch
方法捕獲。
async
函數的語法不難,難在錯誤處理上。
先來看下面的例子:
let a; async function f() { await Promise.reject('error'); a = await 1; // 這段 await 並無執行 } f().then(v => console.log(a));
如上面所示,當 async
函數中只要一個 await
出現 reject 狀態,則後面的 await
都不會被執行。
解決辦法:能夠添加 try/catch
。
// 正確的寫法 let a; async function correct() { try { await Promise.reject('error') } catch (error) { console.log(error); } a = await 1; return a; } correct().then(v => console.log(a)); // 1
若是有多個 await
則能夠將其都放在 try/catch
中。
依然是經過 babel
來使用。
只須要設置 presets
爲 stage-3
便可。
安裝依賴:
npm install babel-preset-es2015 babel-preset-stage-3 babel-runtime babel-plugin-transform-runtime
修改.babelrc
:
"presets": ["es2015", "stage-3"],
"plugins": ["transform-runtime"]
這樣就能夠在項目中使用 async
函數了。