【源码共读】如何优雅的处理 Promise 的错误

news2024/12/22 22:26:48

Promise解决了优雅的解决了回调地域的问题,现在已经大范围的使用Promise,但是在使用Promise的过程中,最令人头疼的就是错误处理的方式。

Promise 的错误处理方式

据我对Promise的了解,Promise的错误处理分为下面的几种方式,如果有还有其他的处理方式,欢迎大家指出。

1. try catch

try catch是最常见的错误处理方式:

try {await Promise.reject('error')
} catch (error) {console.log(error)
} 

但是这种方式有一个致命的缺点,就是只能捕获同步的错误,对于异步的错误,是无法捕获的。

2. Promise.prototype.catch

Promise提供了catch方法,用于捕获Promise的错误:

Promise.reject('error').catch((error) => {console.log(error)
}) 

这种方式是我比较常用的方式,因为它不仅可以捕获同步的错误,还可以捕获异步的错误:

async function test() {await Promise.reject('error')
}

test().catch((error) => {console.log(error)
}) 

3. Promise.prototype.then

Promise提供了then方法,用于捕获Promise的成功和失败:

Promise.reject('error').then((value) => {console.log(value)},(error) => {console.log(error)}
) 

这种方式我放在catch的后面,是因为我很少用,有时候我感觉它很鸡肋,因为它只能处理Promise执行体的错误,而不能处理自己本身的错误:

// 这样是可以的
Promise.reject('error').then((value) => {console.log(value)},(error) => {console.log(error)})

// 这样是不行的
Promise.resolve('error').then((value) => {throw new Error('error')},(error) => {console.log(error)}) 

链式调用

上面说的几种错误处理方式都是对Promise的单次调用,怎么样处理都是可以的;

但是Promise的出现就是为了解决回调地域的问题,所以我们经常会使用链式调用的方式:

await Promise.reject('error');
await Promise.reject('error');
await Promise.reject('error'); 

通常我们会像上面这样,通过await来简化链式调用的负担,但是这样的话,我们对错误的处理就会变得很麻烦:

  • 统一使用一个大的try catch来捕获所有的错误
try {await Promise.reject('error');await Promise.reject('error');await Promise.reject('error');
} catch (error) {console.log(error);
} 

但是这样的话,我们就无法知道是哪个Promise出现了错误,无法保证后续的Promise能够正常执行。

  • 使用Promise.prototype.catch来捕获每个Promise的错误
const errorHandle = (error) => {console.log(error);
};

await Promise.reject('error').catch(errorHandle);
await Promise.reject('error').catch(errorHandle);
await Promise.reject('error').catch(errorHandle); 

这种方式就是我经常使用的方式,但是这样我需要为每一个Promise都写一个错误处理函数。

  • 链式处理
const await1 = async () => {await Promise.reject('error');
};

const await2 = async () => {await Promise.reject('error');
};

const await3 = async () => {await Promise.reject('error');
};

await1().then(await2).then(await3).catch((error) => {console.log(error);}); 

这种方式其实和使用一个大的try catch是一样的,不过还可以衍生出下面的方式:

await1().then(await2).catch(errorHandle).then(await3).catch(errorHandle);

// 或者
await1().then(await2, errorHandle).then(await3, errorHandle).catch(errorHandle); 

显然这样很麻烦,然后还可以借助Promise.all或者Promise.race来简化:

Promise.all([await1(), await2(), await3()]).then(([data1, data2, data3]) => {console.log('success');}) 

当然Promise.all的特点大家都清楚,如果其中一个Promise出现错误,那么整个Promise都会失败,然后就有了Promise.race

Promise.race([await1(), await2(), await3()]).then(() => {console.log('success');}) 

这样的话,只要有一个Promise成功,那么整个Promise就会成功,弥补了Promise.all的不足。

当然还是有很多人不满足,因为懒是天性,就是不想写那么多的Promise,也不想写那么多的catch,于是就有了今天的主角await-to-js

源码分析

await-to-js的源码很简单,就是在内部对错误进行了处理,然后返回一个正确的promise

/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
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];});
} 

从代码上可以看到await-to-js的使用方式很简单,只需要在to函数中传入一个Promise,然后就可以得到一个数组,数组的第一个元素是错误信息,第二个元素是成功的数据。

const [error, data] = await to(Promise.reject('error')); 

它的第二个参数是一个对象,可以传入一些额外的信息,这个信息会被合并到错误信息中。

内部实现也很简单,就是我们上面讲过的错误处理方式其中一种,使用Promise.prototype.catch来捕获错误,然后返回一个正确的Promise

不过就是对返回的结果进行规范化处理,返回一个数组,第一个元素是错误信息,第二个元素是成功的数据。

使用

通过看源码我们就知道了await-to-js的使用方式,那么我们来看看它的使用场景:

const await1 = async () => {await Promise.reject('error');
};

// 可以忽略错误
const [, data] = await to(await1);

// 可以无视
await to(await1);
await to(await1);
await to(await1);

// 可以捕获错误
const [error, data] = await to(await1);
if (data) {// do something
}

// 可以类柯里化
const p1 = to(await1)
const p2 = to(p2)
const p3 = to(p3)
const [, data] = await to(p3) 

怎么用自己舒服就怎么用,还可以探索别的使用方式,让操作骚起来。

总结

await-to-js的实现方式很简单,就是对一个promise进行错误处理,然后返回一个正确的promise;

但是部分人可能会对每次判空感觉很麻烦,那么可以考虑将这个方法修改一下,返回自己喜欢的方式就可以了。

例如将返回数组改为对象,通过错误码来判断是否成功,这样就可以不用每次都判空了。

或者再发挥自己过度封装的能力,将判空也封装起来,哈哈哈。

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

324页13万字高校数字化校园大数据中心及大数据平台建设方案

一、 数据中心总体规划 云资源中心加大数据分析与高性能主要分为计算资源、内存资源、存储资源、网络资源,大数据分析系统,高性能作业调度系统,本项目在充分整合XXX高校数据中心资源的基础上,配置必要软硬件设备,为XXX…

我国军靴行业现状分析:两大利好因素推动市场良好发展

根据观研报告网发布的《中国军靴市场发展趋势分析与投资前景预测报告(2022-2029年)》显示,军靴是指供军事单位在行军,作战时穿着的鞋靴,一般采用头层牛皮制作,具有耐用、舒适以及良好的防水透气效果特点。军…

对Python的学习【如何查看路径和安装包】

1:怎么查看本地电脑的Python版本号及安装路径: 对于Windows平台,打开cmd 使用命令py -0p 【其中0是零】 显示已安装的 python 版本且带路径的列表,参见下图: 其中带星号*的为默认版本。 2:怎么查看python pip…

Fragment

Fragment简单认识 1.简介 在大屏幕设备上支持更加动态和灵活的UI设计就是一种卡片的设计思路一个Activity可以有多个Fragment,一个Fragment可以被多个Activity使用可以进行动态的添加,替换和删除Fragment有着自己的生命周期,同时受到Activity…

python循环语句(三)

一.while 循环 条件满足无限执行 (1) 定义格式 while 条件&#xff1a;条件为True时重复执行# 写法要求与if语句类似使用示例&#xff1a; i 0 while i < 100:print("观止study")i 1 # 等效于 i i 1 # 需要设置循环终止的条件&#xff0c;如i 1配合 i <…

【Pandas入门教程】如何读取和写入表格数据

如何读取和写入表格数据 来源&#xff1a;Pandas官网&#xff1a;https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html 文章目录如何读取和写入表格数据导包【1】如何读取和写入表格数据【2】小结导包 import pandas as pd数据介绍&#xff1a; 使用存…

【信管5.2】估算活动资源与持续时间

估算活动资源与持续时间在经过上次课程的学习后&#xff0c;我们已经了解到了进度、活动的概念及定义&#xff0c;并且简单地学习了下活动顺序如何排列的一些工具技术。今天&#xff0c;我们学习的主要方向是估算活动资源与估算活动持续时间这两个过程&#xff0c;另外我们还会…

python:写你的第一个爬虫代码

什么是爬虫 爬虫spider&#xff0c;是指向网站或者网络发出请求&#xff0c;获取资源后分析并提取对自己有用的数据的程序。 request&#xff1a;是指用户将自己的信息通过浏览器发送给服务器。 response&#xff1a;服务器收到用户的请求分析后&#xff0c;返回的数据。 注意&…

【Pandas入门教程】如何选择DataFrame的子集

如何选择DataFrame的子集 来源&#xff1a;Pandas官网&#xff1a;https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html 文章目录如何选择DataFrame的子集导包数据准备【1】如何从DataFrame中选择特定列&#xff1f;【2】如何从DataFrame中筛选特定行【…

读研2年,我选择从中科院退学转行做码农

从入学天坑材料专业到退学 先自我介绍一下&#xff1a;我天坑材料专业&#xff0c;本科某211&#xff0c;保研到中科院&#xff0c;但是我真是菜的抠脚&#xff0c;还懒&#xff0c;也不喜欢科研&#xff0c;论文达不到毕业要求&#xff0c;纠结之下研三退学转码农了。 读了2…

基于51单片机的电子秤(5KG+上限报警)

电子秤5KG上限警报设计 原理图&#xff1a; 程序运行图&#xff1a; 部分程序&#xff1a; #include "main.h" #include "LCD1602.h" #include "HX711.h" #include "EEPROM.H" //定义变量 unsigned char KEY_NUM 0; //用来存放按…

Java并发——ThreadLocal总结

概述 并发问题&#xff0c;有时候&#xff0c;可以用ThreadLocal方式来避免。 ThreadLocal&#xff0c;顾名思义&#xff0c;就是线程自己的&#xff0c;独享的&#xff0c;就像线程栈是线程独享的一样。 本文讨论三点&#xff1a; 基本用法设计原理父子线程 基础用法 考虑…

JMS规范及落地产品

目录 一、JMS是什么 二、MQ中间件的其他落地产品 三、JMS的组成结构和特点 四、JMS的可靠性 &#xff08;重要&#xff09; &#xff08;一&#xff09;PERSISTENT&#xff1a;持久性 &#xff08;二&#xff09;事务 &#xff08;三&#xff09;Acknowledge&#xff1a…

C#,图像二值化(01)——二值化算法综述与二十三种算法目录

图像二值化&#xff0c;就是把彩色&#xff08;先转为灰色图&#xff09;最终转为黑白两色图片的计算过程。 看似极其简单&#xff0c;但人们研究了几十年&#xff0c;却始终未达到至臻境界的问题。 本文简要介绍了 图像二值化的算法原理、分类及二十三种算法的目录&#xff…

freeswitch的gateway配置方案

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 在voip的网络模型中&#xff0c;网关是我们经常会遇到的概念。 在freeswitch中&#xff0c;如何配置gateway&#xff0c;如何使用好gateway的模型和功能。 本节简单介绍fs中gateway相关的配置方案。 环境 centos&am…

csp-202209

202209题目一&#xff1a;如此编码【100分】题目二&#xff1a;何以包邮&#xff1f;【100分】题目三&#xff1a;防疫大数据【100分】题目一&#xff1a;如此编码【100分】 比较简单的题&#xff0c;根据题意计算一遍就行 一定要关注csp题目中的提示&#xff0c;这个是很有用…

达梦数据IPO过会:拟募资24亿 光谷“扫地僧”冯裕才将敲钟

雷递网 雷建平 12月23日武汉达梦数据库股份有限公司&#xff08;简称&#xff1a;“达梦数据”&#xff09;日前IPO过会&#xff0c;准备在科创板上市。达梦数据计划募资23.51亿元。其中&#xff0c;3.52亿元用于集群数据库管理系统升级项目&#xff0c;3.43亿元用于高性能分布…

[翻译+笔记]变分自编码器:从AutoEncoder到Beta-VAE

与GAN的那篇笔记相同, 做一下笔记. 并不是全文翻译, 只翻译一部分. 原文地址: from AutoEncoder to Beta-VAE 0. 前言 自编码器是用来重构高维数据的&#xff0c;它利用一个有bottleneck层的神经网络。bottleneck层获取压缩的潜在编码&#xff0c;这样将嵌入向量以低维表示可…

Activity生命周期

Activity生命周期 1.Activity状态 1.基本状态 运行&#xff0c;active。位于最前台&#xff0c;可以和用户交互的激活状态。暂停&#xff0c;pause&#xff0c;被透明或者Dialog覆盖&#xff0c;此时可见失去焦点但是不允许交互。停止&#xff0c;stop&#xff0c;被Active覆盖…

spring提前加载,懒加载,bean的作用域和注入注解讲解

前言 sping知识随笔笔记&#xff1b;spring提前加载&#xff0c;懒加载&#xff0c;bean的作用域和注入注解讲解 这里写目录标题前言1 depends-on2 bean的作用域3 lazy-init 懒加载4 Autowrite和Resource的区别和使用1 depends-on depends-on 是提前加载&#xff0c;比如在实…