当前位置: 首页 > 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;进行账号注册&…...

【游戏】【客户端性能测试】

待续… 一、 常见指标 1. 越高越好 FPS 2. 越低越好 网络流量CPU内存&#xff08;PSS&#xff0c; momo&#xff09;Drawcalls三角形数耗电量包体大小 二、 游戏体验 1. 直接体感 游戏花屏闪退卡顿延迟 2. 可能原因 内存超标Drawcall数量多FPS波动严重CPU占用高居不下…...

软考中级-数据库-3.3 数据结构-树

定义:树是n(n>=0)个结点的有限集合。当n=0时称为空树。在任一非空树中,有且仅有一个称为根的结点:其余结点可分为m(m>=0)个互不相交的有限集T1,T2,T3...,Tm…,其中每个集合又都是一棵树,并且称为根结点的子树。 树的相关概念 1、双亲、孩子和兄弟: 2、结点的度:一个结…...

typora高亮方案+鼠标侧键一键改色

引言 在typora里面有一个自定义的高亮, <mark></mark>>但是单一颜色就太难看了, 我使用人工智能, 搜索全网艺术家, 汇集了几种好看的格式,并且方便大家侧键一键 调用, 是不是太方便啦 ! 示例 午夜模式 春意盎然 深海蓝调 石墨文档 秋日暖阳 蜜桃宣言 使用方法 …...

【CSS】Tailwind CSS 与传统 CSS:设计理念与使用场景对比

1. 开发方式 1.1 传统 CSS 手写 CSS&#xff1a;你需要手动编写 CSS 规则&#xff0c;定义类名、ID 或元素选择器&#xff0c;并为每个元素编写样式。 分离式开发&#xff1a;HTML 和 CSS 通常是分离的&#xff0c;HTML 中通过类名或 ID 引用 CSS 文件中的样式。 示例&#…...

Linux(Centos 7.6)命令详解:vim

1.命令作用 vi/vim 是Linux 系统内置不可或缺的文本编辑命令&#xff0c;vim 是vi 的加强版本&#xff0c;兼容vi 的所有指令&#xff0c;不仅能编辑文本&#xff0c;而且还具有shell 程序编辑的功能&#xff0c;可以不同颜色的字体来辨别语法的正确性。 2.命令语法 usage: …...

记录一次wifi版有人物联串口服务器调试经过

1、首先买了一个华为的wifi路由器&#xff0c;连接上以后&#xff0c;设置好网络名字和wifi密码 2、用网线连接串口服务器&#xff0c;通过192.168.1.1登录&#xff0c;进行配置 找到无线客户端配置&#xff0c;先在基本配置中打开5G配置&#xff0c;然后再去5.8G配置中设置 …...

QWQ大模型评测榜单

评测榜单说明 在数学推理基准AIME24上&#xff0c;QwQ-32B达到了79.5分&#xff0c;几乎与DeepSeek-R1-617B的79.8分持平&#xff0c;远超OpenAI o1-mini的63.6分&#xff0c;及相同尺寸的R1蒸馏模型。 在编程能力方面&#xff0c;QwQ-32B 在LiveCodeBench上获得了63.4分&…...

【CXX】5.4 属性

命名空间 顶层的 cxx::bridge 属性宏接受一个可选的命名空间参数&#xff0c;用于控制生成外部 Rust 项的 C 命名空间&#xff0c;以及期望找到外部 C 项的命名空间。 #[cxx::bridge(namespace "path::of::my::company")] mod ffi {extern "Rust" {type…...

c++雅兰亭库 (yalantinglibs) 介绍及使用(序列化、json和结构体转换、协程

c雅兰亭库 (yalantinglibs) 介绍及使用(序列化、json和结构体转换、协程)-CSDN博客 雅兰亭库(yalantinglibs)介绍 雅兰亭库&#xff0c;名字很优雅&#xff0c;也很强大。它是阿里开源的一个现代C基础工具库的集合, 现在包括 struct_pack, struct_json, struct_xml, struct_yam…...

3.6 登录认证

登录功能 登录思路 联调测试 登录校验 问题&#xff1a;在未登录情况下&#xff0c;我们也可以直接访问部门管理、员工管理等功能。 登录标记 用户登录成功之后&#xff0c;每一次请求中&#xff0c;都可以得到该标记。 统一拦截 过滤器Filter拦截器Interceptor 会话技术 会…...