ES7 提出的async
函數,終於讓 JavaScript 對於異步操做有了終極解決方案。No more callback hell。async
函數是 Generator
函數的語法糖。使用 關鍵字 async
來表示,在函數內部使用 await
來表示異步。
想較於 Generator,Async
函數的改進在於下面四點:git
Aysnc
函數自帶執行器,調用方式跟普通函數的調用同樣async
和 await
相較於 *
和 yield
更加語義化co
模塊約定,yield
命令後面只能是 Thunk 函數或 Promise對象。而 async
函數的 await
命令後面則能夠是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操做)async
函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator 對象方便,能夠直接使用 then()
方法進行調用先定義一個 Fetch 方法用於獲取 github user 的信息:github
function fetchUser() {
return new Promise((resolve, reject) => {
fetch('https://api.github.com/users/superman66')
.then((data) => {
resolve(data.json());
}, (error) => {
reject(error);
})
});
}
複製代碼
Promise 方式npm
/**
* Promise 方式
*/
function getUserByPromise() {
fetchUser()
.then((data) => {
console.log(data);
}, (error) => {
console.log(error);
})
}
getUserByPromise();
複製代碼
Promise 的方式雖然解決了 callback hell,可是這種方式充滿了 Promise的 then()
方法,若是處理流程複雜的話,整段代碼將充滿 then
。語義化不明顯,代碼流程不能很好的表示執行流程。
Generator 方式json
/**
* 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()
的方式去執行。
async 方式api
/**
* async 方式
*/
async function getUserByAsync(){
let user = await fetchUser();
return user;
}
getUserByAsync()
.then(v => console.log(v));
複製代碼
async
函數完美的解決了上面兩種方式的問題。流程清晰,直觀、語義明顯。操做異步流程就如同操做同步流程。同時 async
函數自帶執行器,執行的時候無需手動加載。bash
async 函數返回一個 Promise 對象babel
async
函數內部 return 返回的值。會成爲 then
方法回調函數的參數。異步
async function f() {
return 'hello world'
};
f().then( (v) => console.log(v)) // hello world
複製代碼
若是 async
函數內部拋出異常,則會致使返回的 Promise 對象狀態變爲 reject
狀態。拋出的錯誤而會被 catch
方法回調函數接收到。async
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
複製代碼
若是返回的是 reject 的狀態,則會被 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
函數了。