async函數就是Generator函數的語法糖。async函數的實現原理就是Generator函數 + 自動執行器包裝在一個函數裏。es6
asycn函數 | Generator函數 |
---|---|
自帶執行器 | 執行必須靠執行器 |
async/await語義清晰 | 星號和yield語義不明確 |
await命令後Promise對象和原型類型值 | Thunk函數或Promise |
返回值Promise,可使用then命令 | 返回值Iterator對象 |
aysnc函數返回一個Promise對象,aysnc函數內部return語句返回值,做爲then方法回調函數的參數。當函數執行時候,一旦遇到await就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。ajax
function sleep(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}
async function foo(ms){
await sleep(ms);
return 300;
}
foo(2000).then((data)=> {
console.log(data)
});
//300
複製代碼
調用foo會當即返回一個promise,2s後await後面的promise狀態變成resolve,才執行return,執行then,打印300。promise
因爲async返回是promise,程序能夠改成bash
async function sleep(ms) {
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}
//...其餘代碼同上
複製代碼
await命令後是一個Promise對象。若是不是,會被轉成一個當即resolve的Promise。若是await命令後面是一個thenable對象(即定義then方法的對象),那麼await會將其等同於 Promise 對象。能夠參照es6之Promise深刻理解併發
let thenable = {
then: (resolve) => {
console.log('thenable');
resolve('xxx');
}
}
async function foo(){
return await thenable;
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
//thenable
//xxx
複製代碼
內部拋出錯誤,後面語句不執行,直接外部catch捕獲錯誤異步
async function foo(){
throw new Error('出錯了');
await sleep(); //不執行
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
//Error: 出錯了
複製代碼
這種狀況會致使返回的Promise對象狀態爲reject狀態,錯誤被catch方法回調函數接收到。async
async函數返回的Promise對象必須等到內部全部await命名後面的Promise對象執行完纔會發生狀態改變,除非遇到return語句或者拋出錯誤。函數
任何一個await語句後面的Promise對象變爲reject狀態,那麼整個async函數都會中斷執行。post
內部本身捕獲,程序繼續往下執行return語句ui
async function foo(){
await Promise.reject('error').catch((e)=>{
console.log('in:', e.toString())
});
return 300;
}
foo().then((data)=> {
console.log(data)
}).catch((e)=>{
console.log('out:',e.toString())
});
//in: error
//300
複製代碼
const NUM_RETRIES = 3;
async function multiRetries(url, count){
let c = count || NUM_RETRIES;
for(let i = 0; i < c; i++){
try {
await ajax.get(url);
break; //請求成功就跳出循環
} catch (error) {
console.error(error);
}
}
}
multiRetries('http://xxxx').then((data)=> {
console.log(data)
}).catch((e)=>{
console.log(e.toString())
});
複製代碼
多個await命令後面的異步操做,若是不存在繼發關係,最好讓他們同時觸發
let [foo, bar] = await Promise.all([ajax.get(), ajax.post()])
//或
let fooPromise = ajax.get();
let barPromise = ajax.get();
let foo = await fooPromise;
let bar = await fooPromise;
複製代碼
forEach中使用await中注意,以下是併發的。由於只有async函數內部是繼發執行的,外部不受影響。
urls.forEach(async function(url){
await ajax.get(url);
});
複製代碼
Iterator 接口是一種數據遍歷的協議,只要調用遍歷器對象的 next 方法就會獲得一個對象。特別注意 next 方法必須是同步的,只要調用就必須馬上返回值。
Generator函數裏面的異步操做返回一個Thunk函數或者Promise對象,等待之後返回真正的值,done屬性仍是同步產生的。
async function foo(){
try {
for await (let value of createAsyncIterable()){
console.log(value);
}
} catch (e) {
console.log(e.toString());
}
}
複製代碼
await後面操做的應該返回Promise對象。使用yield關鍵字的地方,就是next方法停下來的地方。
async function* gen(){
yield await Promise.resolve('xxx');
}
複製代碼
注:讀阮一峯老師的《ES6入門標準》作的筆記