解析 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实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2