TS Option类型与Promise

 用专门的数据类型描述异常,能便于串联可能出错的操作。Option类型就是这张容器,在没有值时也能串联操作。

js中常用Promise来处理异步调用,本文将手动实现Promise的部分功能,来探索这个类型的处理逻辑。

1 处理错误

TS表示和处理错误的常用模式有:

1)返回null。

2)抛出异常。

3)返回异常。

4)Option类型。

这些处理机制各有千秋,可根据实际的业务场景来选择使用。

1.1 处理错误的常规模式

1.1.1 返回null

在遇到错误时,直接返回null,其他情况下返回正常的值。

function parseDate() {let num = Math.random();if (num > 0.5) {  // 模拟遇到错误的场景return null;}return new Date();
}

在调用此类函数时,需要先检查它的返回结果,然后再使用。

let result = parseDate();
if (result) {console.log(result);
} else {console.log("出错啦");
}

这种方式是处理错误最为轻量的方式,但是无法返回出错原因,且返回的null也不利于程序的编写,每次操作都需要检查返回结果是否为null太繁琐,不利于嵌套和串联操作。

1.1.2 抛出异常

当遇到错误时直接抛出异常。

function parseDate() {let num = Math.random();if (num > 0.5) {  // 模拟遇到错误的场景throw new RangeError("随机数值小于0.5");}return new Date();
}

在调用此类函数时,需要使用try/catch来捕获错误。

try {console.log(parseDate());
} catch (error) {console.error(error);
}

这种方式可以指出失败原因,但是需要使用try/catch来捕获。这样在串联或嵌套操作时将会使代码结构变得复杂。而在实际开发中,开发人员可能不会将代码放在try/catch中,不会去检查异常。

1.1.3 返回异常

在遇到错误时,直接return 异常。

function parseDate() {let num = Math.random();if (num > 0.5) {  // 模拟遇到错误的场景return  new RangeError("随机数值小于0.5");}return new Date();
}

在调用此类函数时,需要根据返回值类型做不同的处理。

let result = parseDate();
if (result instanceof Error) {console.error("出错了:" + result.message);
} else {console.log(result);
}

这种方式比较轻量,又能提供错误信息,能强制使用方处理每一个异常。但是在串联和嵌套操作时会比较繁琐。

1.2 Option类型

Option不是JS内置的数据类型,需要我们自己编写。它表示不返回一个值,而是返回一个容器。该容器可能有一个值,也可能没有。这个容器有一些方法,让没有值时也能串联操作。

class CustomOption<T> {constructor(private val: T) {}then<U>(fun: (val:T) => CustomOption<U>) {return fun(this.val);}getValue(errorMessage: string) {return this.val || errorMessage;}
}function askInput() {let num = Math.random();if (num > 0.5) {return new CustomOption(null)}return new CustomOption("2023-12-16 12:34:24");
}function parseDate(str: string | null) {if (str) {return new CustomOption(new Date(str));} else {return new CustomOption(null);}
}let result = askInput().then(parseDate).getValue("转化失败");
console.log(result);

上面代码有两个问题:1)实际上当第一个函数报错的时候就意味着整个调用链都出错了,因此在后面的调用上,应该及时知晓上个返回值是个空值,以免执行不必要的操作。2)在调用getValue这个参数时,当前一个函数返回正常值时是不需要往getValue传递参数的。

下面是优化后的代码:

interface CustomOption<T> {then<U>(fun: (val:T) => CustomOption<U>): CustomOption<U>;getValue(val:T):T;
}class CustomSome<T> implements CustomOption<T> {constructor(private value: T) {}getValue(): T {return this.value;}then<U>(fun: (val:T) => CustomOption<U>): CustomOption<U> {return fun(this.value);}
}class CustomNone implements CustomOption<never> {getValue<T>(val: T): T {return val;}then<U>(): CustomOption<U> {return this;}
}function askInput() {let num = Math.random();if (num > 0.5) {return new CustomNone()}return new CustomSome("2023-12-16 12:34:24");
}function parseDate(str: string | null) {if (str) {return new CustomSome(new Date(str));} else {return new CustomNone();}
}let result = askInput().then(parseDate).getValue("转化失败");
console.log(result);

2 处理回调

JS异步程序的核心基础是回调。回调其实就是常规函数,只是作为参数传给另一个函数。就像在同步程序一样,另一个函数在操作完成后调用会调函数。

但在串联和嵌套操作中,容易导致“回调金字塔”。

function askInput(callback: (val: string) => void) {let num = Math.random();if (num > 0.5) {throw new RangeError("随机数小于0.5");} else {callback("2023-12-13 12:00");}
}function parseDate(str: string,callback: (date: Date) => void) {callback(new Date(str));
}askInput((val: string) => { // 嵌套第一层if (val) {parseDate(val,(date:Date) => { // 嵌套第二层console.log(date);})}
});

2.1 Promise

在JS中,常用Promise来解决“回调金字塔”问题。

1)实现Promise的then方法来处理回调。

type Executor = (resolve: ExecutorResolve,reject: ExecutorReject
) => voidtype CustomPromiseStatus = "pending" | "finished" | "rejected";
type ExecutorResolve = (val: any)=>void;
type ExecutorReject = (error: any) =>void;class CustomPromise {private status: CustomPromiseStatus;private value: any;private error: null;private resolveCallbackList: Array<ExecutorResolve>;private rejectCallbackList: Array<ExecutorReject>;constructor(executor: Executor) {this.status = "pending";this.value = null;this.error = null;this.resolveCallbackList = [];this.rejectCallbackList = [];const resolve:ExecutorResolve = (val: any) => {this.status = "finished";this.value = val;try {this.resolveCallbackList.forEach(callback => callback(this.value));} catch (e) {reject(e);}};const reject:ExecutorReject = (error: any) => {this.status = "rejected";this.error = error;this.rejectCallbackList.forEach(callback => callback(error));}executor(resolve,reject);}then(resolve:ExecutorResolve,reject: ExecutorReject) {switch (this.status) {case "finished":try {resolve(this.value);} catch (e) {reject(e);}break;case "rejected":reject(this.error);break;default:this.resolveCallbackList.push(resolve);this.rejectCallbackList.push(reject);}}
}function askInputPromise() {return new CustomPromise((resolve, reject) => {let num = Math.random();if (num > 0.5) {reject(new RangeError("随机数小于0.5"));} else {resolve("2023-12-16 12:34:24");}})
}let promise = askInputPromise();
promise.then((val:string) => {console.log(new Date(val));
},(error) => {console.log("出错啦:" + error);
})

2)then 方法返回Promise类型,实现可串联使用。

type Executor = (resolve: ExecutorResolve,reject: ExecutorReject
) => voidtype CustomPromiseStatus = "pending" | "finished" | "rejected";
type ExecutorResolve = (val: any)=> any;
type ExecutorReject = (error: any) =>void;let IsPromise = (val: any) : val is CustomPromise => {return val instanceof CustomPromise;
};class CustomPromise {private status: CustomPromiseStatus;private value: any;private error: null;private resolveCallbackList: Array<ExecutorResolve>;private rejectCallbackList: Array<ExecutorReject>;constructor(executor: Executor) {this.status = "pending";this.value = null;this.error = null;this.resolveCallbackList = [];this.rejectCallbackList = [];const resolve:ExecutorResolve = (val: any) => {this.status = "finished";this.value = val;try {this.resolveCallbackList.forEach(callback => callback(this.value));} catch (e) {reject(e);}};const reject:ExecutorReject = (error: any) => {this.status = "rejected";this.error = error;this.rejectCallbackList.forEach(callback => callback(error));}executor(resolve,reject);}then(resolve:ExecutorResolve,reject?: ExecutorReject) {return new CustomPromise((resolveNew, rejectNew) => {const resolveFun:ExecutorResolve = (val: any) => {try {let res = resolve(this.value);if (IsPromise(res)) {res.then(resolveNew);} else {resolveNew(res);}} catch (e) {rejectNew(e);}};const rejectFun: ExecutorReject = (error: any) => {if (reject) {reject(error);}if (rejectNew) {rejectNew(error);}}switch (this.status) {case "finished":try {resolveFun(this.value);} catch (e) {rejectFun(e);}break;case "rejected":rejectFun(this.error);break;default:this.resolveCallbackList.push(resolveFun);this.rejectCallbackList.push(rejectFun);}})}
}function askInputPromise() {return new CustomPromise((resolve, reject) => {let num = Math.random();if (num > 0.5) {reject(new RangeError("随机数小于0.5"));} else {resolve("2023-12-16 12:34:24");}})
}askInputPromise().then((val:string) => {return new CustomPromise((resolve, reject) => {console.log("输入参数:" + val);resolve(new Date(val));})
}).then((val:any) => {console.log("最终结果:",val)
},(error: any) => {console.log("在这里就出错了",error);
});

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

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

相关文章

linux下解决 git clone每次都要输入用户名密码问题(推荐)

一条命令解决问题&#xff1a; git config --global credential.helper store使用此命令后还会需要输入一次用户名和密码&#xff0c;但是下一次就不需要了 1.在~/目录下多出两个文件.gitconfig 和.git-credentials &#xff0c;用来记录你的密码和帐号 git config --global…

C语言:求和1+1/2-1/3+1/4-1/5+……-1/99+1/100

#include<stdio.h> int main() {int i 0;double sum 0.0;int flag 1;for (i 1;i < 100;i){sum 1.0 / i * flag;flag -flag;}printf("sum%lf\n", sum);return 0; }

设计模式——策略模式

引言 策略模式是一种行为设计模式&#xff0c; 它能让你定义一系列算法&#xff0c; 并将每种算法分别放入独立的类中&#xff0c; 以使算法的对象能够相互替换。 问题 一天&#xff0c; 你打算为游客们创建一款导游程序。 该程序的核心功能是提供美观的地图&#xff0c; 以…

10Maxwell 增量表首日全量同步

通常情况下&#xff0c;增量表需要在首日进行一次全量同步&#xff0c;后续每日再进行增量同步&#xff0c;首日全量同步可以使用Maxwell的bootstrap功能&#xff0c;方便起见&#xff0c;下面编写一个增量表首日全量同步脚本。 在~/bin目录创vim mysql_to_kafka_inc_init.sh …

【每日一题】使用最小花费爬楼梯

文章目录 Tag题目来源解题思路方法一&#xff1a;动态规划空间优化 写在最后 Tag 【动态规划空间优化】【数组】【2023-12-17】 题目来源 746. 使用最小花费爬楼梯 解题思路 方法一&#xff1a;动态规划 思路 假设数组 cost 的长度为 n&#xff0c;则 n 阶楼梯分别对应下标…

【数据库设计和SQL基础语法】--查询数据--分组查询

一、分组查询概述 1.1 什么是分组查询 分组查询是一种 SQL 查询技术&#xff0c;通过使用 GROUP BY 子句&#xff0c;将具有相同值的数据行分组在一起&#xff0c;然后对每个组应用聚合函数&#xff08;如 COUNT、SUM、AVG等&#xff09;。这允许在数据集中执行汇总和统计操作…

Python往事:ElementTree的单引号之谜

最近在针对某款设备的界面xml进行更新过程中&#xff0c;被告知回稿的字串放在了一个excel文件中&#xff0c;而我要上传到服务器的界面用语是用xml文件封装的。再经过详细求证了翻译组提供excel文件的原因后&#xff0c;我决定用python来完成界面用语xml的更新&#xff0c;但是…

OOD 异常GPT:使用大型视觉语言模型检测工业异常

paper link https://arxiv.org/abs/2308.15366video demo https://youtu.be/lcxBfy0YnNAgithub https://github.com/CASIA-IVA-Lab/AnomalyGPT在线使用 https://huggingface.co/spaces/FantasticGNU/AnomalyGPT 摘要 大型视觉语言模型&#xff08;LVLM&#xff09;如MiniGPT-4…

大数据CloudSim应用实践

CloudSimExampleA.java 1准备 1.1操作系统 本实验在Windows 7 或Windows 10系统运行均可。 1.2软件 cloudsim-3.0.3.zip&#xff1b; commons-math3-3.2-bin.zip&#xff1b; jdk-8u152-windows-x64.exe&#xff1b; eclipse-jee-neon-3-win32-x86_64 所需资料链接&#xff1…

W25Q64(模拟SPI)读写数据的简单应用

文章目录 一、W25Q64是什么&#xff1f;二、使用步骤1.硬件1.引脚说明2.硬件连接3.设备ID4.内部框架5.指令集指令集1指令集2 2.软件1.W25Q64引脚定义代码如下&#xff08;示例&#xff09;&#xff1a;2.W25Q64初始化代码如下&#xff08;示例&#xff09;&#xff1a;3.W25Q64…

【IC前端虚拟项目】MVU模块方案与背景熟悉

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 mvu这个模块是干嘛用的呢&#xff1f;从这个名字就可以看出来move_unit&#xff0c;应该是做数据搬运的。很多指令级中都会有数据搬运的指令&#xff0c;这类指令的作用一般是在片内片外缓存以及通用专用…

Skywalking告警规则示例

skywalking告警规则文件配置示例【包含自定义规则】: # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ow…

【C++多线程编程】(二)之详解锁(lock)和解锁(unlock)

在C多线程编程中&#xff0c;锁&#xff08;lock&#xff09;和解锁&#xff08;unlock&#xff09;通常用于管理共享资源的访问&#xff0c;以防止多个线程同时对资源进行修改&#xff0c;从而避免竞态条件&#xff08;Race Condition&#xff09;和数据不一致性问题。C标准库…

Java基础语法之抽象类和接口

抽象类 什么是抽象类 并不是所有的类都是用来描述对象的&#xff0c;这样的类就是抽象类 例如&#xff0c;矩形&#xff0c;三角形都是图形&#xff0c;但图形类无法去描述具体图形&#xff0c;所以它的draw方法无法具体实现&#xff0c;这个方法就可以没设计成抽象方法&…

常用模块之(time/datetime)

【 一 】时间模块&#xff08;time/datetime&#xff09; 【 二 】 表示时间的三种方式 *时间戳&#xff08;Timestamp&#xff09;是指1970年1月1日00:00:00开始计算的偏移量。可以使用time模块中的time()函数获取当前时间的时间戳&#xff0c;也可以使用datetime模块中的tim…

大创项目推荐 深度学习 python opencv 实现人脸年龄性别识别

文章目录 0 前言1 项目课题介绍2 关键技术2.1 卷积神经网络2.2 卷积层2.3 池化层2.4 激活函数&#xff1a;2.5 全连接层 3 使用tensorflow中keras模块实现卷积神经网络4 Keras介绍4.1 Keras深度学习模型4.2 Keras中重要的预定义对象4.3 Keras的网络层构造 5 数据集处理训练5.1 …

ElasticSearch学习篇8_Lucene之数据存储(Stored Field、DocValue、BKD Tree)

前言 Lucene全文检索主要分为索引、搜索两个过程&#xff0c;对于索引过程就是将文档磁盘存储然后按照指定格式构建索引文件&#xff0c;其中涉及数据存储一些压缩、数据结构设计还是很巧妙的&#xff0c;下面主要记录学习过程中的StoredField、DocValue以及磁盘BKD Tree的一些…

PyQt6 QScrollBar滚动条控件

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计48条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

录制第一个jmeter性能测试脚本2(http协议)_图书管理系统

我们手工编写了一个测试计划&#xff0c;现在我们通过录制的方式来实现那个测试计划。也就是说‘’测试计划目标和上一节类似&#xff1a;让5个用户在2s内登录图书管理系统&#xff0c;然后进入 页面进行查看。 目录 欢迎访问我的免费课程 PPT、安装包、视频应有尽有&#xff…

什么是幂等操作?

在数学和计算机科学中&#xff0c;幂等操作是指无论应用操作一次还是多次&#xff0c;结果都相同的操作。 换句话说&#xff0c;重复多次相同的操作不会产生不同的效果。这个概念通常用于描述函数、操作或系统的性质。 在具体应用中&#xff0c;幂等性具有以下两个重要的特性&…