解析 JavaScript 异步编程:从回调地狱到 Promise 和 Async/Await

2024-02-04 19:36

本文主要是介绍解析 JavaScript 异步编程:从回调地狱到 Promise 和 Async/Await,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

在现代的JavaScript开发中,处理异步任务变得愈发重要,因为它们允许我们在等待I/O、网络请求或定时器等事件时继续执行其他任务,以提高程序的性能和响应能力。本文将介绍JavaScript中异步编程的演变过程,从最初的回调地狱到后来的Promise和Async/Await。

回调地狱

回调地狱是一种异步编程的反模式,它在嵌套的回调函数中处理多个异步任务,导致代码难以阅读和维护。以下是一个简单的回调地狱演示:

function add(a: number, b: number, callback: (res: number) => void): void  {setTimeout(() => {callback(a + b);}, 1000);
}// 回调地狱示例
add(2, 3, res => {console.log('2 + 3 = ', res);add(res, 4, res => {console.log('2 + 3 + 4 = ', res);add(res, 5, res => {console.log('2 + 3 + 4 + 5 = ', res);});});
});

如上所示,每个异步任务的结果都在回调函数中处理,形成了嵌套的结构,使得代码难以理解。

Promise

Promise是一种用于处理异步任务的对象,它代表了一个异步操作的最终完成或失败,并提供了更优雅的解决方案,避免了回调地狱的问题。

async function add2(a: number, b: number): Promise<number>  {return new Promise((resolve, reject) => {setTimeout(() => {resolve(a + b);}, 2000);});
}// Promise链式调用
add2(2, 3).then((res: number )=> {console.log('2 + 3 = ', res);return add2(res, 4);}).then((res: number )=>{console.log('2 + 3 + 4 = ', res);return add2(res, 4);}).then((res: number) => {console.log('result:', res);}).catch(e => console.error(`ERROR: ${e}`));

通过Promise,我们可以将异步任务按顺序组织在链式调用中,使得代码更具可读性。

Promise的异常捕获

Promise还提供了异常捕获机制,使得我们可以方便地处理异步任务的错误。

add2(2, 3).then((x: number )=> Promise.reject(10)).then((x: number )=> add2(x, 6)).then((res: number) => console.log(res)).catch(e => console.error(`ERROR: ${e}`));

在这个例子中,当第一个Promise返回了一个拒绝状态时,异常会被捕获,而不会继续执行后续的Promise。

Promise的组合

Promise.all和Promise.race是Promise的两个重要方法。Promise.all等待所有Promise都完成,而Promise.race在第一个Promise完成时就终止。

// Promise同时等待后做事情
Promise.all([add2(2, 3), add2(4, 5)]).then(([a , b]) => {console.log('result', a, b);return mul(a, b);
}).then((res: number) =>console.log('result:', res)
);// Promise谁先做事情就用谁
Promise.race([add2(2, 3), mul(4, 5)]).then((res: number) =>console.log('result:', res)
);

这两个方法使我们能够更灵活地组合和控制多个异步任务的执行。

Async/Await

Async/Await是ES2017引入的异步编程语法糖,使得异步代码更加像同步代码一样,进一步提高了可读性。

async function main(): Promise<void> {try {const result1 = await add2(2, 3);console.log('2 + 3 = ', result1);const result2 = await add2(result1, 4);console.log('2 + 3 + 4 = ', result2);const result3 = await add2(result2, 5);console.log('2 + 3 + 4 + 5 = ', result3);} catch (error) {console.error(`ERROR: ${error}`);}
}main();

使用Async/Await,我们可以更清晰地表达异步任务的执行顺序,避免了回调地狱和过多的Promise链。

总的来说,JavaScript异步编程的演进经历了从回调地狱到Promise和最终的Async/Await的过程,这些新的语法和模式为开发者提供了更强大和清晰的工具,使得异步代码更易于编写和维护。希望本文对你理解JavaScript异步编程有所帮助。

JavaScript异步编程的最新进展:从Promise.allSettled到Promise.any

在ES2020和ES2021中,JavaScript引入了一些新的异步编程方法,进一步改善了处理Promise的体验。本文将介绍其中两个方法:Promise.allSettledPromise.any

Promise.allSettled(ES2020)

在ES2020中,JavaScript引入了 Promise.allSettled 方法,它用于处理多个Promise,无论它们是被解决(fulfilled)还是被拒绝(rejected)。

function asyncFunction1(): Promise<string> {return new Promise(resolve => setTimeout(() => resolve('Result from asyncFunction1'), 1000));
}function asyncFunction2(): Promise<number> {return new Promise(resolve => setTimeout(() => resolve(42), 500));
}// 使用 allSettled 扩展方法
const promises = [asyncFunction1(), asyncFunction2()];Promise.allSettled(promises).then(results => {results.forEach(result => {if (result.status === 'fulfilled') {console.log(`Fulfilled: ${result.value}`);} else {console.log(`Rejected: ${result.reason}`);}});
});

Promise.allSettled 返回一个新的Promise,该Promise在所有传入的Promise都已经被解决或被拒绝后才被解决。它返回一个包含每个Promise结果的对象数组,每个对象都包含 statusvaluereason 属性。

这使得我们能够轻松地处理多个Promise的状态,而不必担心其中一个Promise被拒绝而导致整个操作失败。

Promise.any(ES2021)

在ES2021中,JavaScript引入了 Promise.any 方法,它用于处理多个Promise,只要其中至少有一个被解决。

function asyncFunction3(): Promise<string> {return new Promise(resolve => setTimeout(() => resolve('Result from asyncFunction3'), 1000));
}function asyncFunction4(): Promise<number> {return new Promise((_, reject) => setTimeout(() => reject('Error from asyncFunction4'), 500));
}// 使用 any 扩展方法
const promises2 = [asyncFunction3(), asyncFunction4()];Promise.any(promises2).then(result => {console.log(`Resolved: ${result}`);// 处理至少一个 Promise 被 resolved 的逻辑}).catch(errors => {console.error(`Rejected with all errors: ${errors}`);// 处理所有 Promise 都被 rejected 的逻辑});

Promise.any 返回一个新的Promise,该Promise在传入的Promise中至少有一个被解决后就被解决。它只要有一个Promise被解决,就会返回该Promise的解决值。

这个方法很有用,因为它提供了一种在多个异步任务中找到第一个成功解决的任务的方式。

这两个新方法为JavaScript开发者提供了更多处理异步任务的选择,使得我们能够更灵活地应对各种异步场景。希望这篇文章对你理解Promise.allSettledPromise.any有所帮助。

这篇关于解析 JavaScript 异步编程:从回调地狱到 Promise 和 Async/Await的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/678584

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain