当前位置: 首页 > news >正文

第三课:异步编程核心:Callback、Promise与Async/Await

Node.js 是一个基于事件驱动的非阻塞 I/O 模型,这使得它非常适合处理高并发的网络请求。在 Node.js 中,异步编程是一项非常重要的技能。理解和掌握异步编程的不同方式不仅能提高代码的效率,还能让你更好地应对复杂的开发任务。本文将深入探讨 Node.js 中常见的三种异步编程方式:回调函数(Callback)、Promise 和 async/await。通过比较它们的用法和特点,我们能够选择最适合的方式来处理异步任务,并解决其中可能遇到的问题。

1. 单线程与事件循环机制解析

1.1 单线程模型

Node.js 采用单线程模型,这意味着它一次只能执行一个任务。这种设计简化了多线程环境下的复杂问题,如资源竞争和死锁等。然而,单线程也意味着 Node.js 无法利用多核 CPU 的优势。为了解决这个问题,Node.js 使用了事件驱动和非阻塞 I/O 模型。

1.2 事件循环机制

事件循环是 Node.js 处理异步操作的关键机制。它允许 Node.js 在等待 I/O 操作完成的同时继续执行其他任务。事件循环的工作流程大致如下:

  1.  执行同步代码:执行栈中的同步代码。
  2. 处理异步任务:将异步任务(如 I/O 操作、定时器等)放入相应的队列(宏任务队列或微任务队列)。
  3. 执行微任务:在当前执行栈为空时,执行微任务队列中的所有任务。
  4. 执行宏任务:执行一个宏任务,然后再次清空微任务队列,如此循环。
console.log('Start');
setTimeout(() => {console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {console.log('Promise');
});
process.nextTick(() => {console.log('nextTick');
});
console.log('End');// 输出顺序: Start End nextTick Promise setTimeout

2. 回调地狱问题与解决方案

2.1 回调地狱问题

回调函数是最早在 JavaScript 中用于处理异步操作的一种方式。然而,当需要处理多个异步操作时,回调函数可能会导致“回调地狱”(callback hell)。多个嵌套的回调函数会使代码层级深、逻辑混乱,难以维护。

const fs = require('fs')
fs.readFile('file1.txt', 'utf8', (err, data1) => {if (err) throw err;fs.readFile('file2.txt', 'utf8', (err, data2) => {if (err) throw err;fs.readFile('file3.txt', 'utf8', (err, data3) => {if (err) throw err;console.log(data1, data2, data3);});});
});
2.2 解决方案
2.2.1 使用Promise

Promise 是为了解决回调地狱问题而引入的。它表示一个异步操作的最终完成(或失败)及其结果值。通过链式调用 .then() 方法,可以清晰地组织异步代码。

const fs = require('fs').promises;fs.readFile('file1.txt', 'utf8').then((data1) => {return fs.readFile('file2.txt', 'utf8');}).then((data2) => {return fs.readFile('file3.txt', 'utf8');}).then((data3) => {console.log(data1, data2, data3);}).catch((err) => {console.error('Error:', err);});
2.2.2 使用async/await

async/await 是基于 Promise 的语法糖,它让异步代码看起来更像同步代码,从而提高可读性。

const fs = require('fs').promises;async function readFiles() {try {const data1 = await fs.readFile('file1.txt', 'utf8');const data2 = await fs.readFile('file2.txt', 'utf8');const data3 = await fs.readFile('file3.txt', 'utf8');console.log(data1, data2, data3);} catch (err) {console.error('Error:', err);}
}readFiles();

3. Promise链式调用与错误处理

3.1 Promise链式调用

Promise 允许我们进行链式调用,每一个 .then() 方法都会返回一个新的 Promise,这样就可以继续处理异步操作。

function getDataFromAPI() {return new Promise((resolve, reject) => {setTimeout(() => {const data = { id: 1, name: 'Alice' };resolve(data);}, 500);});
}function processData(data) {return new Promise((resolve, reject) => {setTimeout(() => {const processedData = { ...data, role: 'Admin' };resolve(processedData);}, 500);});
}function saveData(data) {return new Promise((resolve, reject) => {setTimeout(() => {console.log('Data saved:', data);resolve();}, 500);});
}getDataFromAPI().then(data => processData(data)).then(processedData => saveData(processedData)).catch(error => console.error('Error:', error));
3.2 Promise错误处理

Promise 使用 .catch() 来捕获任何在 .then() 中发生的错误。

Promise.resolve().then(() => {throw new Error('Something went wrong');}).then(() => {console.log('This will not be executed');}).catch((err) => {console.error('Error caught:', err);});

4. Async/Await语法糖实战

4.1 async/await基本用法

async/await 是基于 Promise 的语法糖,它让异步代码看起来更像同步代码。async 修饰函数,表示该函数会返回一个 Promise,而 await 用于等待一个 Promise 完成。

const fs = require('fs').promises;async function readFiles() {try {const data1 = await fs.readFile('file1.txt', 'utf8');const data2 = await fs.readFile('file2.txt', 'utf8');console.log('All files content:', data1, data2);} catch (err) {console.error('Error:', err);}
}readFiles();
4.2 并行与串行执行

使用 Promise.all() 可以并行执行多个异步操作,而使用 await 则会串行执行。

const fs = require('fs').promises;async function readFiles() {try {const [data1, data2] = await Promise.all([fs.readFile('file1.txt', 'utf8'),fs.readFile('file2.txt', 'utf8')]);console.log('All files content:', data1, data2);} catch (err) {console.error('Error:', err);}
}readFiles();

串行执行示例:

const fs = require('fs').promises;async function readFiles() {try {const data1 = await fs.readFile('file1.txt', 'utf8');const data2 = await fs.readFile('file2.txt', 'utf8');console.log('All files content:', data1, data2);} catch (err) {console.error('Error:', err);}
}readFiles();

总结

Node.js 提供了多种处理异步操作的方式,包括回调函数、Promise 和 async/await。每种方式都有其适用场景和优缺点:

  • 回调函数:简单直接,但容易导致回调地狱,难以维护。
  • Promise:提供了链式调用和错误处理机制,解决了回调地狱的问题,但仍有一定的复杂性。
  • async/await:基于 Promise,简洁、易读,是现代 JavaScript 的推荐写法。

根据不同的需求和代码结构,你可以选择最适合的异步编程方式来提高代码的可读性、可维护性和性能。

关注我!!🫵 持续为你带来Python爬虫相关内容。

相关文章:

第三课:异步编程核心:Callback、Promise与Async/Await

Node.js 是一个基于事件驱动的非阻塞 I/O 模型,这使得它非常适合处理高并发的网络请求。在 Node.js 中,异步编程是一项非常重要的技能。理解和掌握异步编程的不同方式不仅能提高代码的效率,还能让你更好地应对复杂的开发任务。本文将深入探讨…...

红果短剧安卓+IOS双端源码,专业短剧开发公司

给大家拆解一下红果短剧/河马短剧,这种看光解锁视频,可以挣金币的短剧APP。给大家分享一个相似的短剧APP源码,这个系统已接入穿山甲广告、百度广告、快手广告、腾讯广告等,类似红果短剧的玩法,可以看剧赚钱&#xff0c…...

C# ArrayPool

ArrayPool<T> 的作用ArrayPool<T> 的使用方式共享数组池自定义数组池 注意事项应用场景 在C#中&#xff0c;ArrayPool<T> 是一个非常有用的工具类&#xff0c;主要用于高效地管理数组的分配和回收&#xff0c;以减少内存分配和垃圾回收的压力。它属于 System…...

Conda 生态系统介绍

引言 Conda 是一个开源的包管理和环境管理系统,最初由 Continuum Analytics 开发,现为 Anaconda 公司维护。它在数据科学和 Python/R 生态中占据核心地位,因其能跨平台(Linux/Windows/macOS)管理依赖关系,并通过虚拟环境隔离不同项目的开发环境。Conda 的生态系统包含多…...

批量将 Word 拆分成多个文件

当一个 Word 文档太大的时候&#xff0c;我们通常会将一个大的 Word 文档拆分成多个小的 Word 文档&#xff0c;在 Office 中拆分 Word 文档是比较麻烦的&#xff0c;我们需要将 Word 文档的页面复制到另外一个 Word 文档中去&#xff0c;然后删除原 Word 文档中的内容。当然也…...

Gravitino源码分析-SparkConnector 实现原理

Gravitino SparkConnector 实现原理 本文参考了官网介绍&#xff0c;想看官方解析请参考 官网地址 本文仅仅介绍原理 文章目录 Gravitino SparkConnector 实现原理背景知识-Spark Plugin 介绍(1) **插件加载**(2) **DriverPlugin 初始化**(3) **ExecutorPlugin 初始化**(4) *…...

react基本功

useLayoutEffect useLayoutEffect 用于在浏览器重新绘制屏幕之前同步执行代码。它与 useEffect 相同,但执行时机不同。 主要特点 执行时机:useLayoutEffect 在 DOM 更新完成后同步执行,但在浏览器绘制之前。这使得它可以在浏览器渲染之前读取和修改 DOM,避免视觉上的闪烁…...

python-leetcode-解决智力问题

2140. 解决智力问题 - 力扣&#xff08;LeetCode&#xff09; 这道题是一个典型的 动态规划&#xff08;Dynamic Programming, DP&#xff09; 问题&#xff0c;可以使用 自底向上 的方式解决。 思路 定义状态&#xff1a; 设 dp[i] 表示从第 i 题开始&#xff0c;能获得的最高…...

引领变革!北京爱悦诗科技有限公司荣获“GAS消费电子科创奖-产品创新奖”!

在2025年“GAS消费电子科创奖”评选中&#xff0c;北京爱悦诗科技有限公司提交的“aigo爱国者GS06”&#xff0c;在技术创新性、设计创新性、工艺创新性、智能化创新性及原创性五大维度均获得评委的高度认可&#xff0c;荣获“产品创新奖”。 这一奖项不仅是对爱悦诗在消费电子…...

微信小程序+SpringBoot的单词学习小程序平台(程序+论文+讲解+安装+修改+售后)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统背景 &#xff08;一&#xff09;社会需求背景 在全球化的大背景下&#xff0c;英语作为国际…...

wordpress分类名称调用的几种情况

在WordPress中&#xff0c;如果你想调用当前分类的名称&#xff0c;可以使用single_cat_title()函数。以下是一些常见的使用方法和场景&#xff1a; 1. 在分类页面调用当前分类名称 如果你正在分类存档页面(category.php)中&#xff0c;可以直接使用single_cat_title()函数来…...

HMC7043和HMC7044芯片配置使用

一,HMC7043芯片 MC7043独特的特性是对14个通道分别进行独立灵活的相位管理。所有14个通道均支持频率和相位调整。这些输出还可针对50 Ω或100 Ω内部和外部端接选项进行编程。HMC7043器件具有RF SYNC功能,支持确定性同步多个HMC7043器件,即确保所有时钟输出从同一时钟沿开始…...

html播放本地音乐

本地有多个音乐文件&#xff0c;想用 html 逐个播放&#xff0c;或循环播放&#xff0c;并设置初始音量。 audio 在 html 中播放音乐文件用 audio 标签&#xff1a; controls 启用控制按钮&#xff0c;如进度条、播放、音量、速度等。不加不显示任何 widget。autoplay 理应启…...

Windows11下玩转 Docker

一、前提准备 WSL2&#xff1a;Windows 提供的一种轻量级 Linux 运行环境&#xff0c;具备完整的 Linux 内核&#xff0c;并支持更好的文件系统性能和兼容性。它允许用户在 Windows 系统中运行 Linux 命令行工具和应用程序&#xff0c;而无需安装虚拟机或双系统。Ubuntu 1.1 安…...

vLLM + Open-WebUI 本地私有化部署 DeepSeek-R1-Distill-Qwen-32B 方案

一、vLLM 部署 DeepSeek-R1-Distill-Qwen-32B DeepSeek-R1-Distill 系列模型是 DeepSeek-R1 的蒸馏模型&#xff0c;官方提供了从 1.5B - 70B 不同尺寸大小的模型。特别适合在计算资源有限的环境中部署。 DeepSeek-R1 各个版本的蒸馏模型评估结果如下&#xff1a; 其中 DeepS…...

【基础知识】回头看Maven基础

背景 项目过程中&#xff0c;对于Maven的pom.xml文件&#xff0c;很多时候&#xff0c;我通过各种参考、仿写&#xff0c;最终做出想要的效果。 但实际心里有些迷糊&#xff0c;不清楚具体哪个基础的配置所实现的效果。 今天&#xff0c;特意回过头来&#xff0c;了解Maven的基…...

在 MyBatis 中,若数据库字段名与 SQL 保留字冲突解决办法

在 MyBatis 中&#xff0c;若数据库字段名与 SQL 保留字冲突&#xff0c;可通过以下方法解决&#xff1a; 目录 一、使用转义符号包裹字段名二、通过别名映射三、借助 MyBatis-Plus 注解四、全局配置策略&#xff08;辅助方案&#xff09;最佳实践与注意事项 一、使用转义符号…...

docker-compose Install reranker(fastgpt支持) GPU模式

前言BGE-重新排名器 与 embedding 模型不同,reranker 或 cross-encoder 使用 question 和 document 作为输入,直接输出相似性而不是 embedding。 为了平衡准确性和时间成本,cross-encoder 被广泛用于对其他简单模型检索到的前 k 个文档进行重新排序。 例如,使用 bge 嵌入模…...

200W数据需要去重,如何优化?

优化去重逻辑的时间取决于多个因素&#xff0c;包括数据量、数据结构、硬件性能&#xff08;CPU、内存&#xff09;、去重算法的实现方式等。以下是对优化去重逻辑的详细分析和预期优化效果&#xff1a; 1. 去重逻辑的性能瓶颈 时间复杂度&#xff1a;使用HashSet去重的时间复…...

使用免费IP数据库离线查询IP归属地

一、准备工作 1.下载免费IP数据库 首先&#xff0c;访问 MaxMind官网&#xff08;https://www.maxmind.com/en/home&#xff09;如果你还没有MaxMind账号&#xff0c;可以通过此链接地址&#xff08;https://www.maxmind.com/en/geolite2/signup&#xff09;进行账号注册&…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...