异步调用
异步调用不管是在web端还是在 Node里,都是非常常见的使用方式,简单使用异步调用的时候,其实哪种方式都无所谓,但是逻辑一复杂的时候,就要需要书写更加谨慎,随意地写就会出现自己都搞不清当前这波操作返回什么,还有error catch 不到的情况,web 端和 Node 端都需要 catch 住 error 以作日志分析。
简单的异步调用和 error catch
1、回调函数方式
doFirstThing((err, data) => {
if (err) {
console.log(err);
return;
}
doSecondThing(data, function(err, data){
if (err) {
console.log(err);
return;
}
doThirdThing(data, function(err, data){
if (err) {
console.log(err);
return;
}
})
})
})
2、Promise 方式
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => {
resolve(n + 200)
},)
})
}
function doThing (n) {
return takeLongTime(n);
}
const time1 = 200;
doThing(time1)
.then(time2 => doThing(time2))
.then(time3 => doThing(time3))
.catch(err => {
console.log(err)
})
上面的代码每次异步调用递进200ms,并把下次需要延迟的时间传给下个异步函数
3、async & await
async function doThings () {
try {
const data1 = await doThing(200);
const data2 = await doThing(data1);
const data3 = await doThing(data2);
} catch (err => {
console.log(err)
})
}
async & await 的参数传递方式是不是友好多了
很多人用的时候会疑惑async 和 await 必须都上么?跟 Promise 该如何配合使用?
1)单独使用 async
async function getHello () {
return 'hello world';
}
console.log(getHello())
打印出来如下:
可以看到getHello返回的是一个Promise对象,async 把函数返回的直接量用Promise.resolve('hello world')进行了包裹。
所以我们要拿到"hello world"可以有以下两种方式
// Promise 方式
getHello.then(value => {
console.log(value) // => 输出"hello world"
})
// await 方式
const value = await getHello();
2)await 却不能单独使用
上面的 await getHello()执行的时候实际会如下错误
如图所示,await 在 async 函数中才会有效。
await 是 async wait 的缩写,所以你应该向下面一样wait 你的 getHello
async function asyncGetHello () {
const value = await getHello();
}
3) await等待的不一定要是 async 函数
按照设计,await 等待的是一个异步函数的返回结果,有的人会以为这个异步函数必须是一个 async 函数,但实际上 async 或者一个 Promise实例都是可以的,甚至不是个异步函数,一个直接量也都能正确返回。
async function asyncDemo () {
// await 一个 async 函数
await getHello();
// await 一个 Promise 示例
await new Promise(function (resolve, reject) {
resolve('result');
})
// await 一个直接凉
await 1+1;
}
Promise 的error catch 不足的地方
try{
new Promise(function (reslove, reject) {
throw new Error('有 error 拉');
})
.then(() => {
},err => {
console.log('failCallback',err)
})
.catch(err => {
console.log('promise catch error', err)
})
} catch (err) {
console.log('try catch error', err)
}
代码执行后实际如下
Promise会优先在 failback 里报错,没有 failback 函数,会在 Promise 的catch里执行,永远永远不会跑到try catch 里面。
单个执行没有问题,但是函数里有三四个 Promise 呢,错误处理就变得繁琐和易错,而且只能覆盖自己当前的 Promise 实例。
相比之下,上面 async 和 await 的 try catch 是不是强大和简洁多了。
map和 await 的配合使用
try {
[1,2,3].map(async (data) => {
await doThing();
})
} catch (err) {
console.log('try catch error', err)
}
上面的代码永远不会 catch 到 error
Node 端可以在下面的事件中 catch 住
process.on('unhandledRejection', (reason, p) => {
logger.error('Unhandled Rejection at:', p, 'reason:', reason);
});
但如果我们不想要在unhandledRejection的回调里处理,还是想在 try catch 里统一处理呢,可以用如下方式
try {
async function map&async() {
await Promise.all([1,2,3].map(async (data) => {
await doThing();
}));
}
} catch (err) {
console.log('try catch error', err)
}