百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

如何优雅处理 async await 错误——解读小而美的 await-to-js 库

myzbx 2025-09-09 07:24 18 浏览

原文来自公众号@若川视野,
https://mp.weixin.qq.com/s/a66zzIIo28n7r1AHbNwUhQ

1、前言

学而不思则罔

最近有在读一些比较优秀的npm包的代码,起因是感觉自己现在写的代码还是不够规范,不够简洁。

可是我又不知道到底什么样的代码才算是比较的代码,在进行一番思考过后我认为还是要站在巨人的肩膀上。

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

为什么要在开头写这么多呢?因为我需要为自己坚持下去找一个理由。这样我才能乘风破浪,一往无前。

话不多说,开始总结。

2、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来表示异步。相较于Generatorasync函数的改进在于下面四点:

  • 内置执行器Generator函数的执行必须依靠执行器,而async函数自带执行器,调用方式跟普通函数的调用一样
  • 更好的语义asyncawait相较于*yield更加语义化
  • 更广的适用性co模块约定,yield命令后面只能是 Thunk 函数或 Promise对象。而async函数的await命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)
  • 返回值是 Promiseasync函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用then()方法进行调用

此处总结参考自:理解async/await[1]

有了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进行错误处理是不是感觉不够方便不够智能呢?

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

基本用法

作者是这样介绍这个库的

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

中文翻译过来就是

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

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

function async asyncTask() {
    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)
    }
}

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

import 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和用户自定义和合并到一个对象里面供用户自己选择。

4、结语

源码不可怕,可怕的是自己的面对未知的恐惧感。

敢于面对,敢于尝试,才能更上一层楼。

继续加油,少年。


5、参考资料

  • 仓库地址:https://github.com/scopsy/await-to-js
  • 官方文章:How to write async await without try-catch blocks in Javascript[2]

参考资料

[1]

https://segmentfault.com/a/1190000010244279:https://link.juejin.cn?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000010244279

[2]

How to write async await without try-catch blocks in Javascript:https://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/




相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 &quot;小贱贱&quot;韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...