【源码共读】await-to-js

400 阅读3分钟

await-to-js作用:

是async、await包装器,便于处理错误

Callback 回调函数处理异步

    function AsyncTask(cb){
        asyncFunA(function(err, resultA){
            if(err) return cb(err)
            asyncFunB(function(err, resultB){
                if(err) return cb(err)
                asyncFunC(function, resultC){
                    if(err) return cb(err)
                    ...
                })
            })
        })
    }

我们之前处理异步的方法就像上面的一样,像俄罗斯套娃一样,一层一层,造成了回调地狱。后面我们就有的 Promise。

Promise 异步函数 Promise 是 ES6 提供的方法,目的是解决回调地狱,更优雅的处理复杂的异步任务。 Promise 将之前的回调地狱,改成了链式调用的形式。

function AsyncTask(cb){
    asyncFuncA.then(AsyncFuncB)
    .then(AsyncFuncC) 
    .then(AsyncFuncD) 
    .then(data => cb(null, data)
    .catch(err => cb(err)) 
    }

Promise.all 方法,也支持多个并发请求,获取并发请求中的数据。 但是 promise 也有美中不足的地方:

  • 不够同步
  • 不能很方便的给每一次异步操作进行处理

那么async/await 能够解决:

function async asyncTask(cb) {
    const asyncFuncARes = await asyncFuncA() 
    const asyncFuncBRes = await asyncFuncB(asyncFuncARes) 
    const asyncFuncCRes = await asyncFuncC(asyncFuncBRes)
  }

异步调用时,由于异步函数等待 Promise,当 Promise 遇到错误,就会抛出一个异常,并在最后被 Promise 对象的 catch 方法捕获,使用 async 和 await ,就需要加上 try catch 来捕获错误,如下:

function async asyncTask(cb){
    try {
        const asyncFuncARes = await asyncFuncA()
    } catch(error) {
        return newError(error)
    }
     try {
        const asyncFuncBRes = await asyncFuncB(asyncFuncARes)
    } catch(error) {
        return newError(error)
    }
     try {
        const asyncFuncCRes = await asyncFuncC(asyncFuncBRes)
    } catch(error) {
        return newError(error)
    }
}

但是为每一个 await 都添加 try/catch 显得有点臃肿,那么简化就需要用到 await-to-js 了。

await-to-js 源码

export function to(promise, errorExt){
    return promise
        .then((data) => [null, data])  // 成功,返回 [null, 响应结果]
        .catch((err) => {
            if(errorExt) {
                const parseError = Object.assign({}, err, errorExt)
                return [parsedError, undefined]  // 失败,返回 [错误信息, undefined]
            }
            return [err, undefined]
        })
}

export default to;

源码分析:

接收一个 promise, 将结果解析为一个数组,第一项存入 promise 的异常错误,第二项存入返回的数据。 传入的参数: 第一个是 promise 对象,第二个是可选的错误信息 to 方法会直接返回传入的 promise 的执行 在 then 中返回 [null, data]。null 是执行成功的返回, data 是最终的数据 在 catch 中, 先判断是否传入了额外的错误对象,存在的话,利用 Object.assign 将传入的错误对象与 promise 返回的异常进行合并,然后返回 [prasedError, undefined];不传入额外的错误对象,则直接返回 promise 的异常 [err, undefined]

案例:

async function asyncTask(userId, cb) {
    let err, user, savedTask, notification; 
    [ err, user ] = await to(UserModel.findById(userId)); 
    if(!(user && user.id)) return cb('No user found'); 
    }

总结:

在使用 async 时,需要手动使用 try...catch.. 来捕获错误。而 await-to-js 直接将错误信息返回给用户。to 函数接收两个参数 promise 和 errorExt。 promise 参数接收一个 Promise 对象, 而errorExt 是可选的。Promise对对象,又定义了 then 和 catch 函数,无论成功失败都会返回一个数组。成功(then)时返回 [null, data] data 为成功时的响应结果。失败(catch)时返回[err, undefined]。当 errorExt存在时,返回[parsedError, undefined] , parsedError 是默认错误信息+用户自定义的错误信息。