ES7的async/await語法在2016年就已經(jīng)提出來了,慚愧的是我最近才接觸使用者冤,肤视,下面來聊聊
解決了什么問題
在async/await之前,我們有三種方式寫異步代碼
嵌套回調(diào)
以Promise為主的鏈式回調(diào)
使用Generators
但是涉枫,這三種寫起來都不夠優(yōu)雅邢滑,ES7做了優(yōu)化改進,async/await應運而生愿汰,async/await相比較Promise 對象then 函數(shù)的嵌套困后,與 Generator 執(zhí)行的繁瑣(需要借助co才能自動執(zhí)行,否則得手動調(diào)用next() )衬廷, Async/Await 可以讓你輕松寫出同步風格的代碼同時又擁有異步機制摇予,更加簡潔,邏輯更加清晰吗跋。
async/await特點
async/await更加語義化侧戴,async 是“異步”的簡寫,async function 用于申明一個 function 是異步的跌宛; await酗宋,可以認為是async wait的簡寫, 用于等待一個異步方法執(zhí)行完成秩冈;
async/await是一個用同步思維解決異步問題的方案(等結(jié)果出來之后本缠,代碼才會繼續(xù)往下執(zhí)行)
可以通過多層 async function 的同步寫法代替?zhèn)鹘y(tǒng)的callback嵌套
async function語法
自動將常規(guī)函數(shù)轉(zhuǎn)換成Promise,返回值也是一個Promise對象
只有async函數(shù)內(nèi)部的異步操作執(zhí)行完入问,才會執(zhí)行then方法指定的回調(diào)函數(shù)
異步函數(shù)內(nèi)部可以使用await
async function name([param[, param[, ... param]]]) { statements }
name: 函數(shù)名稱丹锹。
param: 要傳遞給函數(shù)的參數(shù)的名稱
statements: 函數(shù)體語句。
返回值: 返回的Promise對象會以async function的返回值進行解析芬失,或者以該函數(shù)拋出的異常進行回絕楣黍。
await語法
await 放置在Promise調(diào)用之前,await 強制后面點代碼等待棱烂,直到Promise對象resolve租漂,得到resolve的值作為await表達式的運算結(jié)果
await只能在async函數(shù)內(nèi)部使用,用在普通函數(shù)里就會報錯
[return_value] = await expression;
expression: 一個 Promise 對象或者任何要等待的值。
返回值:返回 Promise 對象的處理結(jié)果。如果等待的不是 Promise 對象哩治,則返回該值本身秃踩。
錯誤處理
在async函數(shù)里,無論是Promise reject的數(shù)據(jù)還是邏輯報錯业筏,都會被默默吞掉,所以最好把await放入try{}catch{}中憔杨,catch能夠捕捉到Promise對象rejected的數(shù)據(jù)或者拋出的異常
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模擬出錯,返回error
});
}
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms); //這里返回了錯誤
console.log('end'); //所以這句代碼不會被執(zhí)行了
} catch(err) {
console.log(err); //這里捕捉到錯誤error
}
}
asyncPrint(1000);
如果不用try/catch的話蒜胖,也可以像下面這樣處理錯誤(因為async函數(shù)執(zhí)行后返回一個promise)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模擬出錯消别,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms)
console.log('end'); //這句代碼不會被執(zhí)行了
}
asyncPrint(1000).catch(err => {
console.log(err); // 從這里捕捉到錯誤
});
如果你不想讓錯誤中斷后面代碼的執(zhí)行台谢,可以提前截留住錯誤寻狂,像下面
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, ms); //reject模擬出錯,返回error
});
}
async function asyncPrint(ms) {
console.log('start');
await timeout(ms).catch(err => { // 注意要用catch
console.log(err)
})
console.log('end'); //這句代碼會被執(zhí)行
}
asyncPrint(1000);
使用場景
多個await命令的異步操作朋沮,如果不存在依賴關系(后面的await不依賴前一個await返回的結(jié)果)蛇券,用Promise.all()讓它們同時觸發(fā)
function test1 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
}
function test2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
}
async function exc1 () {
console.log('exc1 start:',Date.now())
let res1 = await test1();
let res2 = await test2(); // 不依賴 res1 的值
console.log('exc1 end:', Date.now())
}
async function exc2 () {
console.log('exc2 start:',Date.now())
let [res1, res2] = await Promise.all([test1(), test2()])
console.log('exc2 end:', Date.now())
}
exc1();
exc2();
exc1 的兩個并列await的寫法,比較耗時朽们,只有test1執(zhí)行完了才會執(zhí)行test2
你可以在瀏覽器的Console里嘗試一下怀读,會發(fā)現(xiàn)exc2的用Promise.all執(zhí)行更快一些
兼容性
在自己的項目中使用
通過 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 函數(shù)了。