ES2017
标準引入了
async
函數,使得異步操作變得更加友善,由于
async函數
傳回的是
Promise
對象,可以作為
await
指令的參數。
async function timeout(ms) {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
傳回 Promise
對象
Promise
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
函數内部抛出錯誤,會導緻傳回的
async
對象變為
Promise
狀态。抛出的錯誤對象會被
reject
方法回調函數接收到
catch
async function f() {
throw new Error('出錯了');
}
f().then(
v => console.log('resolve', v),
e => console.log('reject', e)
)
//reject Error: 出錯了
Promise
對象的狀态變化
Promise
async
函數傳回的
Promise
對象,必須等到内部所有
await
指令後面的
Promise
對象執行完,才會發生狀态改變,除非遇到
return
語句或者抛出錯誤。也就是說,隻有
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('http://localhost:8080/').then(console.log(123))
await
指令
await
正常情況下,
await
指令後面是一個
Promise
對象,傳回該對象的結果。如果不是
Promise
對象,就直接傳回對應的值。
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
任何一個
await
語句後面的
Promise
reject
狀态,那麼整個
async
函數都會中斷執行。
async function f() {
await Promise.reject('出錯了');
await Promise.resolve('hello world'); // 不會執行
}
如果希望即使前一個異步操作失敗,也不要中斷後面的異步操作。這時可以将第一個
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
async
函數的實作原理
async
将 Generator 函數和自動執行器,包裝在一個函數裡
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}