async函数
含义
async是Generator的语法糖
async
函数就是将 Generator 函数的星号(*
)替换成async
,将yield
替换成await
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//等价于
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
基本用法
async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);//指定 50 毫秒以后,输出hello world
async函数有多种适用形式
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};
语法
返回Promise对象
async函数返回一个Promise对象
async
函数内部return
语句返回的值,会成为then
方法回调函数的参数
async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到
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('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"
//上面代码中,函数getTitle内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log
await命令
正常情况下,await命令后面是一个Promise对象,返回该对象的结果
如果不是Promise对象,就直接返回对应的值
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
如果await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于Promise对象
class Sleep {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}
}
(async () => {
const sleepTime = await new Sleep(1000);
console.log(sleepTime);
})();
// 1000
JavaScript一直没有休眠的语法,借助await命令可以让程序停顿指定的时间
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
// 用法
async function one2FiveInAsync() {
for(let i = 1; i <= 5; i++) {
console.log(i);
await sleep(1000);
}
}
one2FiveInAsync();
await后面的Promise对象如果状态变为reject状态,则reject的参数会被catch方法的回调函数接收到
async function f() {
await Promise.reject('出错了');
} //await前面没有return,但是reject方法的参数还是传入了catch方法的回调函数
//如果在await前面加上return,效果是一样的
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了
任何一个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
另一种方法是在await后面的Promise对象再跟一个catch方法
async function f() { await Promise.reject('出错了') .catch(e => console.log(e)); return await Promise.resolve('hello world');}f().then(v => console.log(v))// 出错了// hello world
错误处理
防止错误的方法,就是将其放在try…catch代码块之中
多个await也可以统一放在try…catch结构中
使用注意点
- await后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中
- 多个await命令后面的异步操作,如果不存在继发关系,最好让他们同时触发
- await命令只能用在async函数之中,如果用在普通函数,就会报错