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

  • 内置执行器Generator函数的执行必须依靠执行器,而async函数自带执行器,调用方式跟普通函数的调用一样。
  • 更好的语义asyncawait相较于*yield更加语义化。
  • 更广的适用性co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象。而async函数的await命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)。
  • 返回值是 Promiseasync函数返回值是 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 即可轻松处理错误的异步等待包装器。

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

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

更多内容请看:https://mybj123.com/15987.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/9636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow web入门 php反序列化 web275--web278(无web276)

web275 这道题和序列化一点关系都没有 整个代码并没有说filename(f)怎么传参只有fn并且屏蔽了flag highlight_file(__FILE__);class filter{public $filename;public $filecontent;public $evilfilefalse;public function __construct($f,$fn){$this->filename$f;$this-&g…

bash: docker-compose: 未找到命令

bash: docker-compose: 未找到命令 在一台新的服务器上使用 docker-compose 命令时&#xff0c;报错说 docker-compose 命令找不到&#xff0c;在网上试了一些安装方法&#xff0c;良莠不齐&#xff0c;所以在这块整理一下&#xff0c;如何正确快速的安装 docker-compose cd…

STM32入门周边知识(为什么要装MDK,启动文件是什么,为什么要配置时钟等等)

目录 MDKMDK与C51共存为什么要安装MDK 启动文件是什么&#xff0c;为什么要添加许多文件为什么要添加头文件路径为什么是寄存器配置魔术棒中的define为什么必须先配置时钟杂例 MDK MDK与C51共存 在最开始学习51单片机的时候&#xff0c;当时安装keil的时候&#xff0c;认为就是…

Web实时通信的学习之旅:WebSocket入门指南及示例演示

文章目录 WebSocket的特点1、工作原理2、特点3、WebSocket 协议介绍4、安全性 WebSocket的使用一、服务端1、创建实例&#xff1a;创建一个webScoket实例对象1.1、WebSocket.Server(options[&#xff0c;callback])方法中options对象所支持的参数1.2、同样也有一个加密的 wss:/…

C++ NetworkToHostOrder、HostToNetworkOrder 模板函数

其作用类型&#xff1a;ntohl、htonl、ntohs、htons 函数的作用&#xff0c;因为要考虑兼容 int128、int64 等数据类型。 IPV6 肯定是 int128 了&#xff0c;使用这两个函数可以帮助人们计算IPV6的地址范围等。 template <class T> static T …

在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

在 Hibernate 中&#xff0c;getCurrentSession 和 openSession 是两种不同的方法来获取 Session 对象&#xff0c;它们之间存在一些关键的区别。 事务管理方式&#xff1a; getCurrentSession&#xff1a;它依赖于当前的事务上下文&#xff0c;通常与 Spring 等框架集成&…

报表-设计器的使用

1、设计器目录结构 报表设计器以压缩包的方式提供&#xff0c;解压后&#xff0c;目录结构如下&#xff1a; 目录说明&#xff1a; 1、jdk-17&#xff1a;压缩包中自带的windows平台下的jdk17 2、lite-report&#xff1a;报表文件和数据源配置文件的保存位置 3、lite-repor…

Spring MVC(三) 参数传递

1 Controller到View的参数传递 在Spring MVC中&#xff0c;把值从Controller传递到View共有5中操作方法&#xff0c;分别是。 使用HttpServletRequest或HttpSession。使用ModelAndView。使用Map集合使用Model使用ModelMap 使用HttpServletRequest或HttpSession传值 使用HttpSe…

在AI大模型中全精度和半精度参数是什么意思?

环境&#xff1a; 大模型中 问题描述&#xff1a; 在AI大模型中全精度和半精度参数是什么意思&#xff1f; 解决方案&#xff1a; 在深度学习和高性能计算领域&#xff0c;"全精度"和"半精度"通常指的是模型中使用的数值表示的精度&#xff0c;具体涉…

hadoop学习---基于Sqoop的文件导入导出操作

在本地数据库创建数据库表&#xff1a; create database sqoop_test default character set utf8; use sqoop_test; CREATE TABLE emp ( EMPNO int(4) NOT NULL, ENAME varchar(10), JOB varchar(9), MGR int(4), HIREDATE date, SAL int(7), COMM int(7), DEPTNO int(2), PRI…

eslint关闭的方法总结

package.json关闭eslint 直接注释package.json文件中eslint的配置 "eslintConfig": {"root": true,此项是用来告诉eslint找当前配置文件不能往父级查找"env": {"node": true//此项指定环境的全局变量&#xff0c;下面的配置指定为nod…

Mysql实现双机bin-log热备份

在执行前务必停止对主服务器的mysql数据写入&#xff01;&#xff01;&#xff01; 1.修改主机/etc/my.cnf配置,在mysqld下增加配置&#xff1a; log-bin mysql-bin server-id 1 2.获取MASTER_LOG_FILE、MASTER_LOG_POS信息 登录主机mysql&#xff0c;执行&#xff1a; …

【python基础】python经典题目100题

文章目录 前言初阶题目1.字符串2.列表3.元组4.字典5.运算6.random 模块7.open函数8.time模块时间9.其他 进阶题目 前言 本文主要是python经典题目100题&#xff0c;适合入门的新手。仅作自己学习的记录。 初阶题目 1.字符串 题目1&#xff1a;怎么找出序列中的最⼤最⼩值&am…

STM32使用ESP01S连接阿里云物联网平台

一、ESP01S烧录MQTT固件准备 首先准备好烧录工具&#xff0c;可以从官网上进行下载。 MQTT固件官网网址&#xff1a;AT固件汇总 | 安信可科技 (ai-thinker.com) 进去后如下图界面&#xff0c;向下翻找找到MQTT固件&#xff08;1471&#xff09;下载固件即可。 烧录工具光网地…

windows连接CentOS数据库或Tomcat报错,IP通的,端口正常监听

错误信息 数据库错误&#xff1a; ERROR 2003 (HY000): Cant connect to MySQL server on x.x.x.x (10060) Tomcat访问错误&#xff1a; 响应时间过长 ERR_CONNECTION_TIMED_OUT 基础排查工作 【以下以3306端口为例&#xff0c;对于8080端口来说操作是一样的&#xff0c;只需…

【java毕业设计】基于Spring Boot+vue的网上商城购物系统设计与实现(程序源码)-网上商城购物系统

基于Spring BootVue的网上商城购物系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于Spring BootVue的网上商城购物系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方…

Oracle闪回数据库【Oracle闪回技术】(二)

理解Oracle闪回级别【Oracle闪回技术】(一)-CSDN博客 Oracle默认是不开启闪回数据库的。如果开启闪回数据库的前提条件是,开启Oracle归档模式并启用闪回恢复区。 因为闪回日志文件存放在闪回恢复区中,如果在RAC环境下,必须将闪回恢复区存储在集群文件或者ASM文件中。 一…

揭秘APP广告变现:轻松赚取额外收入!

在移动应用&#xff08;APP&#xff09;的世界中&#xff0c;变现能力是衡量一个应用成功与否的关键指标之一。无论是个人开发者还是企业团队&#xff0c;如何通过应用创造收入&#xff0c;始终是一个备受关注的话题。今天&#xff0c;我们将深入探讨APP广告变现的路径&#xf…

使用nmcli命令在各Linux系统上统一的配置网络

前言&#xff1a;原文在我的博客网站中&#xff0c;持续更新数通、系统方面的知识&#xff0c;欢迎来访&#xff01; 使用nmcli命令在各Linux系统上统一的配置网络https://myweb.myskillstree.cn/123.html 你是否会遇到在不同的Linux系统中配置网络时&#xff0c;修改的配置文…