async函数
在es6中引入了Promise
和 Generator
函数的概念方便我们更加快速优化的实现异步编程,但是生成器的运行依赖于自动执行函数或者co
模块。所以在es7引入了 async
函数,使异步操作更加方便。
基本用法
我们可以把 async
函数看作是自动执行 Generator
函数的语法糖:
async function fn(args) {
}
// 等价于
function fn(args) {
return run(function*() {
});
}
我们只要在普通函数前面加上 async
关键字,在函数体里面把 yield
替换成await
。按正常函数调用方式即可执行按顺序执行异步任务。所以之前的gen()异步任务可以改写成:
function fetchData(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ code: 0, data: url });
}, 1000);
});
}
async function gen() {
let res1 = await fetchData('http://www.baidu.com');
let res2 = await fetchData('http://www.inoob.xyz');
return res1.data + ' ' + res2.data;
}
gen().then(data => console.log(data));
由上面的代码可见,async
函数返回一个 Promise
。函数内部的return值成为then()
成功处理函数的参数。
await
后面如果是一个 Promise
对象,会返回对象的 resolve()
或者reject()
的结果。否则直接返回结果:
async function fn() {
let a = await Promise.resolve(1);
let b = await 2;
return a + b;
}
fn().then(v => console.log(v)); // 3
在 async
函数抛出错误,会被返回的 Promise
的 catch()
方法捕捉:
async function fn() {
await Promise.resolve(1);
throw new Error('boom');
}
fn()
.then(v => console.log(v))
.catch(e => console.log(e.message)); // boom
另外 await
后面的 resolve()
的返回值必须 return
才能被 then()
获取,而 reject()
则不用。并且 reject()
后,函数将会中断执行:
async function fn() {
await Promise.reject(1);
throw new Error('boom'); // 不会执行
}
fn()
.then(null, v => console.log(v)) // 1
.catch(e => console.log(e.message));
错误处理
因为await
后面的 Promise
状态失败会导致整个函数中断,于是可以在try...catch
中捕获:
async function fn() {
try {
await Promise.reject(new Error('boom'));
} catch (e) {
//
}
return await Promise.resolve(1);
}
fn()
.then(v => console.log(v)) // 1
.catch(e => console.log(e.message));
另一种方式是在 await
后面的 Promise
对象的后面再跟上一个 catch()
方法:
async function fn() {
await Promise.reject(new Error('boom')).catch(e => {});
return await Promise.resolve(1);
}
fn()
.then(v => console.log(v)) // 1
.catch(e => console.log(e.message));
并发执行
await
后面的异步任务如果没有先后顺序,最好让他们并发执行:
async function fn() {
let res1 = await getName();
let res2 = await getMaxListeners();
return [res1.data, res2.data];
}
应该写成下面方式:
async function fn() {
let p1 = getName();
let p2 = getMaxListeners();
let res1 = await p1;
let res2 = await p2;
return [res1.data, res2.data];
}
或者利用 Promise.all()
的方式:
async function fn() {
let p1 = getName();
let p2 = getMaxListeners();
let [res1, res2] = await Promise.all([p1, p2]);
return [res1.data, res2.data];
}
异步处理比较
对于 Promise
和 async
两者其实没有什么可比性,后者其实是需要依赖前者。只是有了 async
让异步编码更加优雅:
function fetch() {
return fetchData()
.then(data => {
if (data.moreData) {
return fetchAnotherData(data)
.then(moreData => {
return moreData
})
} else {
return data
}
});
}
// 用async改写
async function fetch() {
const data = await fetchData()
if (data.moreData) {
const moreData = await fetchAnotherData(data);
return moreData
} else {
return data
}
};
对于和 Generator
比较,我认为在异步处理方面是可以取代它的,毕竟方便更多。但是Generator
还有其他方面的应用,比如更加方便的实现迭代器。