异步调用和try catch

2,819 阅读3分钟

异步调用

异步调用不管是在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)
}