最近有一个包里用了不少async await,但是细细看了一下发现很多地方用的比较奇怪,比如同步的函数,没有用到await也用async,就稍微整理了一下相关的使用,没有实现原理相关的内容,以示例为主,有点像面试题。参考了一些文章,就不单独罗列了,一搜还挺多的。
async返回了什么
async返回一个promise,这个大家应该都很清楚,那么看一下这个例子,看看是否能区分
async function sleep(second) {
// 把这个return去掉和不去掉,输出有什么区别
return await new Promise(resolve => {
resolve("sleeped");
});
}
console.log(sleep().then(console.log));
上面这个例子,sleep函数写了return的时候,返回的是await后面的promise,打印出的是sleeped
,去掉return,打印出的是undefined
,可以认为,这时候async返回的是一个隐藏的Promise.resolve(),我们可以用以下例子来验证
async function num(){
return 33;
}
console.log(num());
console.log(Promise.resolve(33));
循环中的async
先来看看for中使用async的姿势
(async function() {
for (let i = 0; i < 3; i++) {
// 这个例子其实是考察Promise的
await new Promise(res => {
res();
console.log(i);
});
}
console.log(123);
})();
(async function() {
for (let i = 0; i < 3; i++) {
await new Promise(res => {
res(i);
// setTimeout(()=>res(i), 3000);
}).then(console.log);
}
console.log(123);
})();
再来看看map中使用async的姿势,使用map有两个地方可以放await,一个是map前面,一个是map内的函数,
(async function() {
await [1,2,3].map(i=>{
// 如果这个例子的结果和你想的不一样,可以想想map返回了啥
// 注意这个return
return new Promise(res => {
res(i);
// 如果写成这样,结果会不会不一样
// setTimeout(()=>res(i), 3000);
}).then(console.log);
})
console.log(123);
})();
(async function() {
[1,2,3].map(async i=>{
// 这里最外层的那个async没啥用
await new Promise(res => {
res(i);
// 如果写成这样,结果会不会不一样
// setTimeout(()=>res(i), 3000);
}).then(console.log);
})
console.log(123);
})();
第一个例子,map返回的其实是一个数组,里面有三个Promise,所以如果想要等待map中的Promise执行完毕,需要在map外面包一个Promise.all。
第二个例子,先输出了123,应该不是一般需要的那种结果。
如果仔细的话,会发现使用定时3秒输出的时候,map的两个例子是3个一起输出的,而for循环的3个输出是一个接一个。
其他的循环类型,可以自己试一试
到底要不要wait
async function sleep(second) {
await new Promise(res=>{
console.log(789)
setTimeout(()=>res(123), 3000);
}).then(console.log);
}
(async function(){
// 这个await去掉和不去掉的区别
await sleep();
console.log(456);
})()
我开始看到这个例子的时候,觉得调用sleep的时候可以不需要await,因为sleep自己带有await,等待Promise执行完毕,但是一试,发现和我想的不一样,如果打印出sleep的返回,就比较清楚了,sleep()返回了一个Promise(这个我们在开头就说了),所以如果不带上await,后面的console是不会等待sleep的,所以如果需要同步调用一个async函数,一定要await,不管它里面写成啥样(如果它内部直接返回常数,可以试试上面那个返回33的例子)
并行的async
在一些情况下我们是需要并行多个Promise的,比如多个请求一起发送,那么就不能一个一个await下来,比较常用的Promise.all肯定没问题,上面提到的map也可以,还发现了一种,别人写的,但是忘了记录是哪篇文章,看一下例子
async function fetchUserParallel () {
const namePromise = new Promise(res=>setTimeout(_=>res('name'),3000))
const avatarPromise = new Promise(res=>setTimeout(_=>res('avatar'),3000))
// 这种形式挺有意思的
return {
name: await namePromise,
avatar: await avatarPromise
}
}
(async function () {
console.time('should be 3s : ')
const user2 = await fetchUserParallel()
console.log(user2)
console.timeEnd('should be 3s : ')
})()
错误处理
最简单和常用的就是try catch,catch有两种写法(可能更多)
try{
await promise();
}catch(){
...
}
await promise().catch(){
...
};
还有一种,也是看来的,很有意思
function transform(promise) {
return promise
.then(data => {
// 其实也可以只返回一个值,通过判断返回值的类型来区分是否出错了
return [null, data];
})
.catch(err => [err]);
}
(async function asyncTask() {
let err, data;
[err, data] = await transform(promiseRequest);
if (err) {
// ...
} else {
// ...
}
})()