如何使用 await-to-js 库优雅的处理 async await 错误

简介: 如何使用 await-to-js 库优雅的处理 async await 错误

通过阅读优秀的源码并从中学习如何写出让人觉得赏心悦目的代码最后再写文章进行总结对整个学习的过程进行一个梳理同时分享给其他人。

JS 异步编程进化之路

回调地狱阶段

在正式介绍 await-to-js 这个库之前,让我们先简单的回顾一下有关于在 JavaScript 这门语言中,异步编程的进化之路。在 Promise 没出现之前,异步编程一直是困扰着前端开发工程师的一个大难题,当时的前辈可能会经常看到下面这种代码。


function AsyncTask() {
   asyncFuncA(function(err, resultA){
      if(err) return cb(err);
      asyncFuncB(function(err, resultB){
         if(err) return cb(err);
          asyncFuncC(function(err, resultC){
               if(err) return cb(err);
               // And so it goes....
          });
      });
   });
}

这种同时在纵向和横向延伸的回调中嵌套着回调的代码又被称为回调地狱。可见这玩意让人多么恶心,具体来说有以下这几个缺点:


难以维护(看都不想看,还维护个**)

难以捕捉到错误(一个一个找?)    总而言之,这个问题在当时是很需要被解决的,所以在 ES6 中,出现了 Promise。

Promise 阶段

Promise 是一种优雅的异步编程解决方案。从语法上来将,它是一个对象,代表着一个异步操作最终完成或失败,从语意上来讲,它是承诺,承诺过一段时间给你一个结果。


由于它的原型存在 then,catch,finally 会返回一个新的 promise 所以可以允许我们链式调用,解决了传统的回调地狱的问题。


由于它本身存在 all 方法,所以可以支持多个并发请求,获取并发请求中数据。


有了 Promise 后,上面的代码可以被写成下面这样。


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

相比较于上面的回调地狱,使用 Promise 可以帮助我们让代码只在纵向发展,并且提供了处理错误的回调。显然优雅了很多。不过就算 Promise 已经这么优秀了,可是依然存在两个每种不足的地方:


不够同步(代码依然会纵向延伸)

不能给每一次异步操作都进行错误处理        这也就是为什么 ES7 中会出现 async / await,号称异步编程的最后解决方案的原因了。

async/await

async函数是Generator函数的语法糖。使用 关键字async来表示,在函数内部使用await来表示异步。相较于Generator,async函数的改进在于下面四点:


内置执行器。Generator函数的执行必须依靠执行器,而async函数自带执行器,调用方式跟普通函数的调用一样。

更好的语义。async和await相较于*和yield更加语义化。

更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象。而async函数的await命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)。

返回值是 Promise。async函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用then()方法进行调用。

有了 async/await,上面的代码可以被改写成下面这样:

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

同时我们可以对每一次异步操作进行错误处理:


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

这样一来上面 Promise 存在的两个每种不足的地方是不是就被优化了呢?所以说 async/await 是 JS 中异步编写的最后解决方案我个人觉得一点问题没有,但是我不知道你看上面的代码,每一次异步操作都要用 try/catch 进行错误处理是不是感觉不够方便不够智能呢?

await-to-js-小而美的 npm 包

基本用法

作者是这样介绍这个库的:


Async await wrapper for easy error handling without try-catch。


中文翻译过来就是:


无需 try-catch 即可轻松处理错误的异步等待包装器。


这里做个简单的对比,之前我们在异步操作中处理错误的方法是这样的:



而用了 await-to-js 之后,我们可以这样的处理错误:


i

mport to from './to.js';
function async asyncTask() {
   const [err, asyncFuncARes]  = await to(asyncFuncA())
   if(err) throw new (error);
 
   const [err, asyncFuncBRes]  = await tp(asyncFuncB(asyncFuncARes))
   if(err) throw new (error);
 
   const [err, asyncFuncCRes]  = await to(asyncFuncC(asyncFuncBRes)
   if(err) throw new (error);
}

是不是简洁多了呢?


作者究竟用了什么黑魔法?


你可能不信,源码只有仅仅 15 行。

源码分析

export function to<T, U = Error> (
  promise: Promise<T>,
  errorExt?: object
): Promise<[U, undefined] | [null, T]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, undefined]>((err: U) => {
      if (errorExt) {
        const parsedError = Object.assign({}, err, errorExt);
        return [parsedError, undefined];
      }
      return [err, undefined];
    });
}

export default to;

上面这里是 TS 版的源码,但是考虑到有些同学可能还没接触过 TS,我着重分析一下下面这版 JS 版的源码。


export function to(promise, errorExt) {
    return promise
        .then((data) => [null, data])
        .catch((err) => {
        if (errorExt) {
            const parsedError = Object.assign({}, err, errorExt);
            return [parsedError, undefined];
        }
        return [err, undefined];
    });
}

export default to;

这里我们先抛开 errorExt 这个自定义的错误文本,核心代码是这样的:


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

export default to;

可以看出,其代码的逻辑用中文解释是这样的


无论成功还是失败都返回一个数组,数组的第一项是和错误相关的,数组的第二项是和响结果相关的


成功的话数组第一项也就是错误信息为空,数组第二项也就是响应结果正常返回


失败的话数组第一项也就是错误信息为错误信息,数组第二项也就是响应结果返回 undefined


经过上面的分析我们可以认定,世界上没有什么黑魔法,没有你做不到,只有你想不到。


这里我们再来看函数 to 的第二个参数 errorExt 不难发现,这玩意其实就是拿来用户自定义错误信息的,通过Object.assign将正常返回的 error 和用户自定义和合并到一个对象里面供用户自己选择。


相关文章
|
17天前
|
监控 JavaScript 前端开发
确定使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript
【10月更文挑战第24天】选择使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript 是一个需要综合考虑多个因素的决策。需要根据脚本之间的依赖关系、页面加载性能要求、脚本的功能和重要性等因素来进行权衡。在实际应用中,需要通过测试和验证来确定最适合的加载方式,以提供更好的用户体验和页面性能。
|
11天前
|
前端开发 JavaScript 开发者
除了 async/await 关键字,还有哪些方式可以在 JavaScript 中实现异步编程?
【10月更文挑战第30天】这些异步编程方式在不同的场景和需求下各有优劣,开发者可以根据具体的项目情况选择合适的方式来实现异步编程,以达到高效、可读和易于维护的代码效果。
|
12天前
|
JavaScript 前端开发 搜索推荐
Moment.js、Day.js、Miment,日期时间库怎么选?
【10月更文挑战第29天】如果你需要一个功能强大、插件丰富的日期时间库,并且对性能要求不是特别苛刻,Moment.js是一个不错的选择;如果你追求极致的轻量级和高性能,那么Day.js可能更适合你;而如果你有一些特定的日期时间处理需求,并且希望在性能和功能之间取得平衡,Miment也是可以考虑的。
|
12天前
|
JSON 前端开发 JavaScript
浅谈JavaScript中的Promise、Async和Await
【10月更文挑战第30天】Promise、Async和Await是JavaScript中强大的异步编程工具,它们各自具有独特的优势和适用场景,开发者可以根据具体的项目需求和代码风格选择合适的方式来处理异步操作,从而编写出更加高效、可读和易于维护的JavaScript代码。
19 1
|
17天前
|
存储 JavaScript 前端开发
decimal.js库的安装和使用方法
【10月更文挑战第24天】decimal.js 是一个非常实用的高精度计算库,通过合理的安装和使用,可以在 JavaScript 中实现精确的数值计算和处理。你可以根据具体的需求和项目情况,灵活运用该库来解决数字精度丢失的问题。
|
1月前
|
JavaScript 前端开发 开发者
掌握Node.js中的异步编程:从回调到async/await
Node.js的异步编程模型是其核心特性之一,它使得开发者能够构建高性能和高并发的应用程序。本文将带你从Node.js的异步编程基础开始,逐步深入到回调函数、Promises、以及最新的async/await语法。我们将探讨这些异步模式的原理、使用场景和最佳实践,并通过实例代码展示如何在实际项目中应用这些概念。
|
1月前
|
JavaScript 前端开发 开发者
jQuery:JavaScript库的瑰宝
jQuery:JavaScript库的瑰宝
41 3
|
1月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:深入了解 Promise 和 async/await
【10月更文挑战第8天】JavaScript 中的异步编程:深入了解 Promise 和 async/await
|
30天前
|
JSON 前端开发 JavaScript
探索JavaScript中的Async/Await:简化异步编程的利器
【10月更文挑战第12天】探索JavaScript中的Async/Await:简化异步编程的利器
16 0
|
1月前
|
JavaScript 前端开发 API
深入了解jQuery:快速、高效的JavaScript库
【10月更文挑战第11天深入了解jQuery:快速、高效的JavaScript库
12 0