async函数
async函数的含义
简单来说:async 函数就是 Generator 函数的语法糖
下面是两个是Generator和async函数依次读取两个文件的写法
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async写法:
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
区别:其实就是把Generator函数的*号换成await。
改进地方:
- async有内置执行器,不用依靠co模块才能执行
- async有更好的语义,从字面上就可以知道是异步的了
- async有更广的适用性,co模块规定,yield命令后面只能是Thunk函数或Promise对象,而async的await命令后main可以是promise对象和原始类型的值
- async返回值是promise:Generator返回值是iterator,所以async可以用then进行链式调用
async的基本用法
async返回值是一个Promise对象,可以使用then方法进行链式调用,当函数执行时,遇到await命令就会先返回,直到异步操作完成后,才继续执行函数后面的内容
多种使用方法:
// 函数声明
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 () => {};
async函数返回一个Promise对象
async函数内容return语句返回值,会成为then方法回调函数的参数
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(v),
e => console.log(e)
)
// Error: 出错了
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对象或原始数据。如下面的await是跟123
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
只要异步函数中,await语句后面的Promise编程reject,那么整个函数都会中断执行。如下例子
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
解决方法:把可能出错的代码放进try chatch结构中,这样不管代码有没有出错,后面的代码依旧会执行。例子如下
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
async的错误处理
如果await命令后面的异步操作出错,那么等同于async函数返回的promise对象被reject
防止出错的方法是将其放进try...catch代码中。如
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
} catch(e) {
}
return await('hello world');
}
注意点
如果多个await命令后面的异步操作不存在继发关系,最好让他们同事触发
let foo = await getFoo();
let bar = await getBar();
上面是写成了继承关系,只有当上一个异步操作完成了,下一个才会开始进行
修改方法:
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
另外,esm模块加载器支持顶层await,即await命令可以不放在async函数里面直接使用
当然,前提你必须使用esm加载器。
例子:
// async 函数的写法
const start = async () => {
const res = await fetch('google.com');
return res.text();
};
start().then(console.log);
// 顶层 await 的写法
const res = await fetch('google.com');
console.log(await res.text());
网友评论