从 await-to-js 到 try-run-js

news2025/4/6 4:48:24

之前在做 code review 时候发现有同事使用 try catch 包装了一堆异步代码,于是个人就觉得很奇怪,难道不应该只 catch 可能出问题的代码吗?同事告诉我说 try catch 太细的话会出现内外作用域不一致,需要提前声明变量。

let res: Data[] = [];

try {res = await fetchData();
} catch (err) {// 错误操作或者终止// return
}

// 继续执行正常逻辑 

的确,一方面开发者不应该大范围包裹非异常代码,另一方面提前声明变量会让代码不连贯同时也会打断思路。其中一个方式是直接使用原生 Promie 而不是 async。

fetchData().then((res) => {
}).catch((err) => {
}); 

这样对于单个异步请求当然没有任何问题,如果是具有依赖性的异步请求。虽然可以再 Promise 中返回另外的 Promise 请求,但是这样处理 catch 却只能有一个。

fetchData().then((res) => {// 业务处理return fetchData2(res);
}).then((res) => {// 业务处理
}).catch((err) => {// 只能做一个通用的错误处理了
}); 

如果需要多个 catch 处理,我们就需要这样写。

fetchData().then((res) => {// 业务处理return fetchData2(res);
}).catch((err) => {// 错误处理并且返回 nullreturn null;
}).then((res) => {if (res === null) {return;}// 业务处理
}).catch((err) => {// 错误处理
}); 

这时候开发者也要考虑 fetchData2 会不会返回 null 的问题。于是个人开始找一些方法来帮助我们解决这个问题。

await-to-js

await-to-js 是一个辅助开发者处理异步错误的库。我们先来看看该库是如何解决我们问题的。

import to from "await-to-js";

const [fetch1Err, fetch1Result] = await to(fetchData());
if (fetch1Err) {// 错误操作或者终止// return
}

const [fetch2Err, fetch1Result] = await to(fetchData2(fetch1Result));
if (fetch2Err) {// 错误操作或者终止// return
} 

源码非常简单。

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];});
} 

使用 try-run-js

看到 await-to-js 将错误作为正常流程的一部分,于是个人想到是不是能通过 try catch 解决一些异步代码问题呢?

我立刻想到了需要获取 DOM 节点的需求。现有框架都使用了数据驱动的思路,但是 DOM 具体什么时候渲染是未知的,于是个人想到之前代码,Vue 需要获取 ref 并进行回调处理。

function resolveRef(refName, callback, time: number = 1) {// 超过 10 次跳出递归if (time > 10) throw new Error(`cannot find ref: ${refName}`);// const self = this;// 获取 ref 节点const ref = this.$refs[refName];if (ref) {callback(ref);} else {// 没有节点就下一次this.$nextTick(() => {resolveRef.call(self, refName, callback, time + 1);});}
} 

当然了,上述代码的确可以解决此类的问题,在处理此类问题时候我们可以替换 ref 和 nextTick 的代码。于是 await-to-js 的逻辑下,个人开发了 try-run-js 库。我们先看一下该库如何使用。

import tryRun from "try-run-js";

tryRun(() => {// 直接尝试使用正常逻辑代码// 千万不要添加 ?.// 代码不会出错而不会重试this.$refs.navTree.setCurrentKey("xxx");
}, {// 重试次数retryTime: 10,// 下次操作前需要的延迟时间timeout: () => {new Promise((resolve) => {this.$nextTick(resolve);});},
}); 

我们也可以获取错误数据和结果。

import tryRun from "try-run-js";

const getDomStyle = async () => {// 获取一步的const { error: domErr, result: domStyle } = await tryRun(() => {// 返回 dom 节点样式,不用管是否存在 ppt// 千万不要添加 ?.// 代码不会出错而返回 undefinedreturn document.getElementById("domId").style;}, {// 重试次数retryTime: 3,// 返回数字的话,函数会使用 setTimeout// 参数为当前重试的次数,第一次重试 100 ms,第二次 200timeout: (time) => time * 100,// 还可以直接返回数字,不传递默认为 333// timeout: 333});if (domErr) {return {};}return domStyle;
}; 

当然了,该库也是支持返回元组以及 await-to-js 的 Promise 错误处理的功能的。

import { tryRunForTuple } from "try-run-js";

const [error, result] = await tryRunForTuple(fetchData()); 

try-run-js 项目演进

try-run-js 核心在于 try catch 的处理,下面是关于 try-run-js 的编写思路。希望能对大家有一些帮助

支持 await-to-js

const isObject = (val: any): val is Object =>val !== null &&(typeof val === "object" || typeof val === "function");

const isPromise = <T>(val: any): val is Promise<T> => {// 继承了 Promise// 拥有 then 和 catch 函数,对应手写的 Promisereturn val instanceof Promise || (isObject(val) &&typeof val.then === "function" &&typeof val.catch === "function");
};

const tryRun = async <T>(// 函数或者 promisepromiseOrFun: Promise<T> | Function,// 配置项目options?: TryRunOptions,
): Promise<TryRunResultRecord<T>> => {// 当前参数是否为 Promiseconst runParamIsPromise = isPromise(promiseOrFun);const runParamIsFun = typeof promiseOrFun === "function";// 既不是函数也不是 Promise 直接返回错误if (!runParamIsFun && !runParamIsPromise) {const paramsError = new Error("first params must is a function or promise");return { error: paramsError } as TryRunResultRecord<T>;}if (runParamIsPromise) {// 直接使用 await-to-js 代码return runPromise(promiseOrFun as Promise<T>);}
}; 

执行错误重试

接下来我们开始利用 try catch 捕获函数的错误并且重试。

// 默认 timeout
const DEFAULT_TIMEOUT: number = 333

// 异步等待
const sleep = (timeOut: number) => {return new Promise<void>(resolve => {setTimeout(() => {resolve()}, timeOut)})
}

const tryRun = async <T>(promiseOrFun: Promise<T> | Function,options?: TryRunOptions,
): Promise<TryRunResultRecord<T>> => {const { retryTime = 0, timeout = DEFAULT_TIMEOUT } = {...DEFAULT_OPTIONS,...options,};// 当前第几次重试let currentTime: number = 0;// 是否成功let isSuccess: boolean = false;let result;let error: Error;while (currentTime <= retryTime && !isSuccess) {try {result = await promiseOrFun();// 执行完并获取结果后认为当前是成功的isSuccess = true;} catch (err) {error = err as Error;// 尝试次数加一currentTime++;// 注意这里,笔者在这里犯了一些错误// 如果没有处理好就会执行不需要处理的 await// 1.如果当前不需要重新请求(重试次数为 0),直接跳过// 2.最后一次也失败了(重试完了)也是要跳过的if (retryTime > 0 && currentTime <= retryTime) {// 获取时间let finalTimeout: number | Promise<any> = typeof timeout === "number"? timeout: DEFAULT_TIMEOUT;// 如果是函数执行函数if (typeof timeout === "function") {finalTimeout = timeout(currentTime);}// 当前返回 Promise 直接等待if (isPromise(finalTimeout)) {await finalTimeout;} else {// 如果最终结果不是 number,改为默认数据if (typeof finalTimeout !== "number") {finalTimeout = DEFAULT_TIMEOUT;}// 这里我尝试使用了 NaN、 -Infinity、Infinity // 发现 setTimeout 都进行了处理,下面是浏览器的处理方式// If timeout is an Infinity value, a Not-a-Number (NaN) value, or negative, let timeout be zero.// 负数,无穷大以及 NaN 都会变成 0await sleep(finalTimeout);}}}}// 成功或者失败的返回if (isSuccess) {return { result, error: null };}return { error: error!, result: undefined };
}; 

这样,我们基本完成了 try-run-js.

添加 tryRunForTuple 函数

这个就很简单了,直接直接 tryRun 并改造其结果:

const tryRunForTuple = <T>(promiseOrFun: Promise<T> | Function,options?: TryRunOptions): Promise<TryRunResultTuple<T>> => {return tryRun<T>(promiseOrFun, options).then(res => {const { result, error } = resif (error) {return [error, undefined] as [any, undefined]}return [null, result] as [null, T]})
} 

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

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

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

相关文章

【微服务】微服务保护Sentinel

微服务保护Sentinel1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel1.4.微服务整合Sentinel2.流量控制2.1.簇点链路2.1.快…

豆瓣引流流程

豆瓣引流注册账号养号如何把豆瓣的帖子打造好并且引流到微信注册账号 第一&#xff1a;可以去营业厅或者卡商那里购买一批卡来进行注册。 第二&#xff1a;可以通过接码平台进行大量的一个小号注册&#xff0c;我们注册的号前期是作为一个顶帖号来使用。 第三&#xff1a;我…

商业智能 BI 跟业务系统的思维差异

我们在跟企业的沟通过程中经常发现&#xff0c;很多企业还是分不清商业智能 BI 跟一般的业务信息化系统定位、用户、思维层面上的差异。因为在企业的IT信息化规划中&#xff0c;基础的业务系统建设一定是走在前面的&#xff0c;有了这些系统基础&#xff0c;才会有数据的积累&a…

Python绘制图片一

文章目录一、代码段讲解1. theta np.linspace(0.0, 2 * np.pi, N , endpointFalse)2. ax plt.subplot(111,projectionpolar)3. bar.set_facecolor(plt.cm.viridis(r / 10.))4. bar.set_alpha(0.5)二、附录一、代码段讲解 1. theta np.linspace(0.0, 2 * np.pi, N , endpoint…

Windows软件:如何使用VMware® Workstation 16 Pro安装Centos7操作系统

前言&#xff1a; 在我们开发Java项目当中&#xff0c;经常会将jar包部署在Linux操作系统中运行&#xff0c;其中Centos7使用最广泛&#xff0c;前后端的各种运行环境所必须的软件均在此上运行&#xff0c;本章我们就来讲一下如何使用VMware安装Centos7系统&#xff0c;以便我们…

08技术太卷我学APEX-页面上显示静态图片

08技术太卷我学APEX-页面上显示静态图片 0 我想在首页面上留个人微信二维码和微信群二维码 我想在《技术太卷我学APEX》首页上留下联系方式&#xff0c;方便同学们加群一起交流联系方式。 先手机登录个人微信&#xff0c;截图个人微信二维码和《技术太卷我学APEX》微信群二维…

【MyBatis持久层框架】MyBatis参数传递详细解读

文章目录1. 前言2. MyBatis 参数传递3. 多个参数4. 单个参数4.1 POJO类4.2 Map集合类4.3 Collection集合类型4.4 List集合类型4.5 Array类型4.6 其他类型5. 总结1. 前言 前面在使用配置文件实现增删改查一文中&#xff0c;我们说到&#xff0c;使用 MyBatis 的 Mapper 代理开发…

glibc memcpy内部机制学习记录

判断需要拷贝的字节数是否大于临界值&#xff08;16或8&#xff09;。如果小于&#xff0c;直接按照one byte by one byte来拷贝。如果大于&#xff1a; 1、先进行内存对齐。假设要拷贝的目的地址如下所示 其中start为拷贝目的地的起始地址 &#xff0c;end为拷贝目的地的结束…

企业的内部文档太杂乱,有什么好用的文档管理软件?

企业内部文档的管理&#xff0c;是一个老生常谈的问题。 有些企业的文档管理比较混乱&#xff0c;很难做好企业内部的信息管控。 我们可以先从以下几个方面入手&#xff1a; 企业内部文档杂乱分散&#xff0c;集中式的管理&#xff1b;信息更新不及时、错误频繁&#xff0c;通过…

大数据NiFi(十六):处理器Connection连接

文章目录 处理器Connection连接 一、查看队列中的FlowFile 二、查看FlowFile自定义属性值

【数据结构初阶】第一节.初识时间和空间复杂度

文章目录 前言 一、认识数据结构 二、时间复杂度 2.1 时间复杂度的概念 2.2 计算时间复杂度 2.2.1 大O的渐进表示法 2.3 常见时间复杂度计算举例 三、空间复杂度 3.1 空间复杂度的概念 3.2 计算空间复杂度 3.3 常见空间复杂度计算举例 四、常见复杂度的对比&#xff1…

CVE-2022-26937 Windows NFS 栈溢出漏洞分析

简介 NFS全称Network File System&#xff0c;即网络文件系统&#xff0c;用于服务器和客户机之间文件访问和共享的通信&#xff0c;从而使客户机远程访问保存在存储设备上的数据。 CVE-2022-26937是微软5月份修复的Windows NFS中一个对NLM响应处理不当的栈溢出漏洞&#xff…

OAuth2(1)

目录 一、什么是OAuth2.0 二、OAuth2中的角色 三、认证流程 四、生活中的OAuth2思维 五、令牌的特点 六、OAuth2授权方式 1.授权码 2.隐藏方式 3.密码方式 4.凭证方式 一、什么是OAuth2.0 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用户授权第三方应用…

红宝书学习

第一章 认识js js的组成部分有哪些&#xff1f; ①ecma 核心语法 api ②dom 提供与网页内容交互的方法和接口 ③bom 浏览器对象模型&#xff0c;提供了和浏览器交互的接口 use strict 是什么&#xff1f; use strict 是一种 ECMAscript5 添加的&#xff08;严格模式&#xff…

玩了半年NFT,一心进军Web3的Prada到底要怎么玩?

图片来源&#xff1a;无界AI绘画工具生成2022年1月&#xff0c;奢侈品品牌Prada与阿迪达斯玩了一把“联合营销”&#xff0c;玩法是这样的&#xff1a;首先&#xff0c;两个品牌邀请粉丝上传个人照片&#xff0c;然后&#xff0c;品牌抽取3000名粉丝的作品&#xff0c;交由数字…

【Rust】12. 自动化测试

12.1 编写测试 12.1.1 测试函数 测试函数&#xff1a;在一个函数前加上一行 #[test] 注解将普通函数变成测试函数 12.1.2 assert! 宏 12.1.3 assert_eq! 与 assert_ne! assert_eq!(left, right) 与 assert_eq!(left, right) 在失败时会返回 left 与 right 两个值&#xff0c…

Python学习-----起步1

目录 Python的下载&#xff08;解释器&#xff09; IDLE进入Python解释器 交互模式 脚本模式 注释 单行注释&#xff1a; 多行注释 Python的下载&#xff08;解释器&#xff09; 百度网盘链接&#xff1a; https://pan.baidu.com/s/1WEmOAGGHtHc1fxZzNGKu6A?pwd5356 …

web3小白入门:区块链的了解

记录web3学习的过程&#xff0c;从小白开始所有的web3相关的学习内容都会更新在github&#xff0c;github地址这篇文章主要说明区块链的一些概念为什么要了解区块链&#xff1f;Web3 是以区块链技术为核心,构建新一代的去中心化互联网组件,再基于它们来构建我们想要提供的服务、…

HTTP状态码301和302区别

Http 状态码 301 和 302 定义&#xff1a; 1、什么时候使用301&#xff1f; 你将永久更改网页的 URL时。你将永久迁移到新域名时。当你从 HTTP 切换到 HTTPS 时。你希望修复非 www / www 重复内容问题时。永久合并两个或多个页面或网站时。你将永久更改网站的 URL 结构时。 …

万字详解递归与递推

秋名山码民的主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f64f;作者水平有限&#xff0c;如发现错误&#xff0c;还请私信或者评论区留言&#xff01; &#x1f44d;目录前言递归斐波那契数列问题的递归爬楼梯问题力扣递归实现…