【Web前端】从回调到现代Promise与Async/Await

异步编程是一种让程序能够在等待某些操作完成的同时继续执行其他任务的关键技术,打破了传统编程中顺序执行代码的束缚。这种编程范式允许开发者构建出能够即时响应用户操作、高效处理网络请求和资源加载的应用程序。通过异步编程,JavaScript 能够在执行耗时操作(如数据库查询、文件读写或网络通信)时,不阻塞主线程,从而保持应用的流畅性和响应性。这种能力对于构建现代Web应用至关重要,因为它使得程序能够在等待异步任务完成的过程中,继续处理用户输入、更新界面或执行其他后台任务,极大地提升了用户体验和系统的整体性能。异步编程的核心在于事件循环和回调机制,共同构成了 JavaScript 异步操作的基础,使得开发者能够以更加灵活和高效的方式管理程序的执行流程。​


一、同步编程:一条单行道

什么是同步编程?

同步编程是一种编程范式,其中程序的执行是顺序的,即代码块按照它们在源代码中出现的顺序依次执行。在同步编程模型中,当一个函数或操作被调用时,它将阻塞调用它的代码的执行,直到该函数或操作完成并返回结果。这意味着在等待函数执行完成之前,程序不会继续执行后续的代码。


同步编程的特点:

  1. 顺序执行:代码按照编写的顺序依次执行,每个操作完成后才会进行下一个操作。
  2. 阻塞调用者:函数调用会阻塞调用它的代码,直到函数执行完毕。
  3. 等待结果:调用者必须等待函数返回结果后才能继续执行。

一个耗时的同步函数示例:

以下是一个简单的同步函数,它模拟了一个耗时的计算过程:

function calculateLargeNumber() {let result = 0;for (let i = 0; i < 1000000000; i++) {result += i;}return result;
}console.log(calculateLargeNumber()); // 这将花费很长时间

在这个例子中,​​calculateLargeNumber​​​ 函数执行了一个非常大的循环,计算了从 0 到 999999999 的累加和。这个过程非常耗时,如果在一个网页或应用程序中调用这个函数,它将导致用户界面冻结,因为浏览器的主线程被这个长时间运行的函数阻塞了。


耗时同步函数的问题:

当同步函数执行时间过长时,会引发以下问题:

  • 阻塞UI线程:在浏览器中,UI线程负责处理用户界面的更新。如果UI线程被长时间运行的同步函数阻塞,用户界面将无法响应用户的操作,导致界面冻结。
  • 用户体验下降:用户可能会遇到界面卡顿、无响应的情况,这会严重影响用户体验,并可能导致用户流失。
  • 资源浪费:长时间运行的同步函数会占用大量的CPU资源,导致其他任务无法高效执行,从而降低整体性能。

为了解决这些问题,异步编程被引入,它允许程序在等待某些操作完成时继续执行其他任务,从而提高程序的响应性和效率。


二、异步编程:并行世界的钥匙

什么是异步编程?

异步编程是一种编程范式,它允许程序在执行一个可能需要较长时间完成的任务时,不会阻塞当前执行流程,从而可以继续处理其他任务或响应用户交互。在异步编程中,程序不会等待某个操作完成后再继续执行,而是立即返回,允许其他代码在等待操作完成的同时运行。


异步编程的核心概念:

  • 非阻塞:异步操作不会阻塞调用它的代码,程序可以继续执行其他任务。
  • 事件驱动:异步编程通常与事件驱动模型结合使用,即程序通过监听事件来响应外部触发的事件。
  • 回调函数:异步操作通常通过回调函数来处理结果,当操作完成时,回调函数会被执行。
  • Promise:Promise 是一种更现代的异步编程模式,它提供了一个更好的方式来处理异步操作的结果。
  • 异步函数:ES2017 引入了异步函数,它允许使用 ​​async​​ 和 ​​await​​​ 关键字来编写看起来像同步代码的异步操作。

异步编程的必要性:

异步编程在许多场景中都是必要的,尤其是在以下情况下:

  1. 网络请求:使用 ​​fetch()​​ 等API发起 HTTP 请求时,网络延迟可能会很长,如果使用同步编程,将会阻塞UI线程,导致界面无响应。
fetch('https://api.example.com/data').then(response => response.json()).then(data => console.log(data)).catch(error => console.error('Error:', error));
  1. 访问硬件设备:使用 ​​getUserMedia()​​ 访问用户的摄像头和麦克风时,需要等待用户授权,这个过程是异步的。
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {// 使用 stream}).catch(error => {// 处理错误});
  1. 文件操作:使用 ​​showOpenFilePicker()​​​ 请求用户选择文件时,需要等待用户选择文件,这是一个异步过程。
showOpenFilePicker().then(fileHandle => {// 使用文件}).catch(error => {// 处理错误});

异步编程的重要性:

即使你不需要经常实现自己的异步函数,正确使用异步编程也是非常重要的。原因如下:

  • 提高性能:异步编程可以避免阻塞UI线程,从而提高应用程序的性能和响应性。
  • 改善用户体验:异步操作可以确保用户界面在等待操作完成时仍然可用,从而提供更好的用户体验。
  • 简化复杂逻辑:异步编程模式可以帮助简化涉及多个异步操作和回调的复杂逻辑。

异步编程是现代Web开发中不可或缺的一部分,它允许程序在执行长时间运行的任务时保持高效和响应。


三、事件处理程序:异步编程的基石

什么是事件处理程序?

事件处理程序是编程中用于响应特定事件发生时执行的一组代码。JavaScript 中事件处理程序是处理用户交互、浏览器行为或其他系统事件的关键机制。


事件处理程序的定义:

事件处理程序(也称为事件监听器或事件处理器)是一段代码,它被绑定到某个对象(如 HTML 元素)上,以便在特定事件发生时自动执行。事件可以是用户操作(如点击、按键、鼠标移动等),也可以是浏览器内部的事件(如页面加载完成、窗口大小改变等)。


事件监听器:

事件监听器是用于添加事件处理程序的机制。在 JavaScript 中,你可以使用 ​​addEventListener​​ 方法来为元素添加事件监听器。以下是如何为按钮点击事件添加事件监听器的示例:

document.getElementById('myButton').addEventListener('click', function() {console.log('按钮被点击了!');
});

​addEventListener​​ 方法被用来为具有 ID ​​myButton​​ 的按钮添加一个点击事件监听器。当按钮被点击时,匿名函数中的代码将被执行,控制台将输出 "按钮被点击了!"。


事件循环:

JavaScript 的执行环境使用事件循环来处理异步事件。事件循环的工作原理如下:

  1. 执行栈:JavaScript 的执行栈是按顺序执行代码的地方。当你在浏览器中编写 JavaScript 代码时,这些代码首先被推入执行栈。
  2. 事件队列:当事件发生时(例如用户点击按钮),事件会被添加到事件队列中。事件队列是一个按顺序存储待处理事件的列表。
  3. 事件处理:当执行栈为空时(即当前执行的函数执行完毕),事件循环会从事件队列中取出下一个事件,并调用相应的事件处理程序。

以下是事件循环的基本步骤:

  • 检查执行栈:JavaScript 引擎检查执行栈是否为空。
  • 处理事件:如果执行栈为空,事件循环从事件队列中取出一个事件。
  • 执行事件处理程序:事件循环调用事件处理程序,该程序可能执行异步操作。
  • 继续循环:事件循环继续检查执行栈是否为空,并重复上述步骤。

事件处理程序的使用场景:

  • 用户交互:响应用户的点击、按键、鼠标移动等操作。
  • 浏览器事件:处理页面加载完成、窗口大小改变、滚动事件等。
  • 网络请求:在异步网络请求完成后处理响应。

事件处理程序是 JavaScript 中实现异步编程和响应式编程的关键工具,它使得 JavaScript 能够创建出动态和交互式的网页应用。


四、回调:异步编程的早期解决方案

什么是回调?

回调(Callback)是函数式编程中的一个概念,它指的是将一个函数作为参数传递给另一个函数,并在适当的时候(通常是异步操作完成后)调用这个传递进来的函数。回调是一种常见的异步编程模式,它允许开发者处理异步操作的结果,而不会阻塞主线程的执行。


回调函数的示例:

以下是一个简单的回调函数的示例,它演示了如何使用回调来处理异步操作:

function fetchData(callback) {// 模拟异步操作,例如从服务器获取数据setTimeout(() => {const data = '这里是异步获取的数据';callback(data); // 在异步操作完成后调用回调函数}, 2000); // 假设异步操作需要2秒钟
}fetchData(function(data) {console.log(data); // 输出: 这里是异步获取的数据// 在这里可以继续处理data
});

在这个例子中,​​fetchData​​ 函数模拟了一个异步操作,它使用 ​​setTimeout​​ 来模拟延迟。当延迟结束后,它调用传递给它的回调函数 ​​callback​​,并传递操作的结果 ​​data​​。


回调地狱(Callback Hell):

尽管回调函数是处理异步操作的一种有效方式,但过度使用回调会导致所谓的“回调地狱”。回调地狱是指在一个函数内部嵌套了多个回调函数,导致代码结构混乱、难以阅读和维护。

以下是一个回调地狱的示例:

fetchData(function(data) {console.log(data);fetchData(function(data) {console.log(data);fetchData(function(data) {console.log(data);// ...更多的回调嵌套});});
});

在这个例子中,每个回调函数都依赖于前一个回调函数的结果,这导致了代码的深度嵌套。当回调层次变得非常深时,代码的可读性和可维护性会大大降低。


回调地狱的解决方案:

为了解决回调地狱的问题,JavaScript 社区提出了多种解决方案,包括:

  • Promise:Promise 是一个对象,它代表了异步操作最终完成(或失败)时的结果。它提供了一种更简洁的方式来处理异步操作,并避免了回调嵌套。
  • async/await:ES2017 引入了 ​​async​​ 和 ​​await​​ 关键字,它们允许开发者以更接近同步代码的方式编写异步代码,从而避免了回调嵌套。

以下是一个使用 Promise 和 async/await 的示例:

// 使用 Promise
function fetchData() {return new Promise((resolve, reject) => {setTimeout(() => {const data = '这里是异步获取的数据';resolve(data);}, 2000);});
}fetchData().then(data => {console.log(data);// 继续处理数据
});// 使用 async/await
async function fetchDataAsync() {const data = await fetchData();console.log(data);// 继续处理数据
}fetchDataAsync();

这些解决方案使得异步代码更加清晰和易于管理,从而提高了代码的可读性和可维护性。


五、Promise:回调的替代品

什么是 Promise?

Promise 是 JavaScript 中用于处理异步操作的一种重要机制。它是一个对象,用于表示一个异步操作最终完成(或失败)时的结果。Promise 提供了一种更现代、更易于管理的异步编程方式,它解决了传统回调函数带来的“回调地狱”问题。


Promise 的核心概念:

  1. 状态:Promise 有三种状态,这些状态在 Promise 的生命周期中只能改变一次:
  • pending:初始状态,表示异步操作尚未完成。
  • fulfilled:操作成功完成,表示异步操作的结果已经可用。
  • rejected:操作失败,表示异步操作出现了错误。
  1. 构造函数:​​Promise​​ 是一个构造函数,用于创建一个新的 Promise 对象。它接受一个执行器函数(executor function),该函数接受两个参数:​​resolve​​ 和 ​​reject​​。​​resolve​​ 函数用于将 Promise 状态从 ​​pending​​ 改变为 ​​fulfilled​​,而 ​​reject​​ 函数用于将状态从 ​​pending​​ 改变为 ​​rejected​​。
  2. 链式调用:Promise 支持链式调用,这意味着你可以将多个 ​​.then()​​ 或 ​​.catch()​​ 方法链接在一起,以便在异步操作完成后按顺序执行多个操作。

使用 Promise 的示例:

以下是一个使用 Promise 的示例,它模拟了一个异步操作,例如从服务器获取数据:

function fetchData() {return new Promise((resolve, reject) => {// 模拟异步操作setTimeout(() => {const data = '这里是异步获取的数据';// 假设操作成功,调用 resolveresolve(data);// 如果操作失败,调用 reject// reject(new Error('发生错误'));}, 2000); // 假设异步操作需要2秒钟});
}// 使用 .then() 处理成功的结果
fetchData().then(data => {console.log(data); // 输出: 这里是异步获取的数据// 在这里可以继续处理 data
});// 使用 .catch() 处理失败的结果
fetchData().then(data => {console.log(data);
}).catch(error => {console.error('发生错误:', error);
});

例子中​​fetchData​​ 函数返回一个 Promise 对象。当异步操作成功完成时,​​resolve​​ 被调用,并且传递给 ​​.then()​​ 方法的回调函数将执行。如果异步操作失败,​​reject​​ 被调用,并且传递给 ​​.catch()​​​ 方法的回调函数将执行。


Promise 的优势:

  • 避免回调地狱:Promise 允许你以链式调用的方式处理异步操作,从而避免了回调嵌套的问题。
  • 更好的错误处理:Promise 提供了 ​​.catch()​​ 方法来集中处理错误,这使得错误处理更加清晰和一致。
  • 易于测试:Promise 使得异步代码更容易被测试,因为它们可以像同步代码一样被断言。

Promise 是现代 JavaScript 异步编程的基础,它为开发者提供了一种更强大、更灵活的方式来处理异步操作。


如有表述错误及不足之处还望各位佬指正补充。

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

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

相关文章

文心一言 VS 讯飞星火 VS chatgpt (388)-- 算法导论24.5 8题

八、设 G ( V , E ) G(V,E) G(V,E) 为一个带权重的有向图&#xff0c;且包含一个可以从源结点 s s s 到达的权重为负值的环路。请说明如何构造一个 G G G 的边的松弛操作的无限序列&#xff0c;使得每一步松弛操作都能对某一个最短路径估计值进行更新。如果要写代码&#x…

uni-app资源管理与图标使用全解

uni-app 框架与资源路径 不需要专门去学习小程序的语法&#xff0c;uni-app使用的是vue的语法&#xff0c;不是小程序自定义的语法。 搜索框&#xff1a;有疑问直接搜索框输入&#xff0c;BUG直接复制错误提示粘贴上去搜索。 介绍&#xff1a;先看这个页面&#xff0c;就知道u…

大数据新视界 -- 大数据大厂之 Impala 性能优化:数据加载策略如何决定分析速度(上)(15/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Java:JVM

1.JVM内存区域的划分 一个Java写的程序跑起来,就得到了一个Java进程 JVM 上面运行的字节码指令; 进程:操作系统资源分配的基本单位; 内存区域的划分: 1.程序计数器 在内存空间里(比较小的空间),保存了下一个要执行的指令的内存地址(元数据区的地址); 这里的"下一条…

阿里公告:停止 EasyExcel 更新与维护

最近&#xff0c;阿里发布公告通知&#xff0c;将停止对知名 Java Excel 工具库 EasyExcel 的更新和维护。EasyExcel 由阿里巴巴开源&#xff0c;作者是玉箫&#xff0c;在 GitHub 上拥有 30k stars、7.5k forks 的高人气。 据悉&#xff0c;EasyExcel 作者玉箫去年已从阿里离…

安卓智能对讲终端|北斗有源终端|三防对讲机|单兵终端|单北斗

在当今快速发展的通信技术时代&#xff0c;智能对讲手持机已成为众多行业领域中不可或缺的通讯工具。QM240T安卓智能对讲手持机&#xff0c;作为一款集先进技术与实用功能于一身的高端设备&#xff0c;凭借其卓越的性能和多样化的应用特性&#xff0c;正逐步引领对讲机市场的革…

5G智能对讲终端|北斗有源终端|北斗手持机|单兵|单北斗

在当今这个快速发展的数字化时代&#xff0c;5G技术的广泛应用正以前所未有的速度推动着各行各业的变革。作为这一技术浪潮中的重要一环&#xff0c;5G智能终端QM630D凭借其卓越的性能和多样化的功能&#xff0c;在林业、渔业、安保、电力、交通等多个领域展现出了巨大的应用潜…

【计网】数据链路层笔记

【计网】数据链路层 数据链路层概述 数据链路层在网络体系结构中所处的地位 链路、数据链路和帧 链路(Link)是指从一个节点到相邻节点的一段物理线路(有线或无线)&#xff0c;而中间没有任何其他的交换节点。 数据链路(Data Link)是基于链路的。当在一条链路上传送数据时&a…

重学SpringBoot3-整合 Elasticsearch 8.x (二)使用Repository

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 整合 Elasticsearch 8.x &#xff08;二&#xff09;使用Repository 1. 环境准备1.1 项目依赖1.2 Elasticsearch 配置 2. 使用Repository的基本步骤2.1 创建实体类2.2 创…

计算机课程管理:Spring Boot与工程认证的协同创新

3系统分析 3.1可行性分析 通过对本基于工程教育认证的计算机课程管理平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于工程教育认证的计算机课程管理平…

<项目代码>YOLOv8 苹果腐烂识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

游戏引擎学习第四天

视频参考:https://www.bilibili.com/video/BV1aDmqYnEnc/ BitBlt 是 Windows GDI&#xff08;图形设备接口&#xff09;中的一个函数&#xff0c;用于在设备上下文&#xff08;device context, DC&#xff09;之间复制位图数据。BitBlt 的主要用途是将一个图像区域从一个地方复…

SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记

这是一篇港科大学生在google research 实习期间发在ECCV2024的语义引导生成式修复的文章&#xff0c;港科大陈启峰也挂了名字。从首页图看效果确实很惊艳&#xff0c;尤其是第三行能用文本调控修复结果牌上的字。不过看起来更倾向于生成&#xff0c;对原图内容并不是很复原&…

如何平滑切换Containerd数据目录

如何平滑切换Containerd数据目录 大家好&#xff0c;我是秋意零。 这是工作中遇到的一个问题。搭建的服务平台&#xff0c;在使用的过程中频繁出现镜像本地拉取不到问题&#xff08;在项目群聊中老是被人出来&#x1f605;&#xff09;原因是由于/目录空间不足导致&#xff0…

Sharding运行模式、元数据、持久化详解

运行模式 单机模式 能够将数据源和规则等元数据信息持久化&#xff0c;但无法将元数据同步至多个Sharding实例&#xff0c;无法在集群环境中相互感知。 通过某一实例更新元数据之后&#xff0c;会导致其他实例由于获取不到最新的元数据而产生不一致的错误。 适用于工程师在本…

基于springboot+小程序的鲜花管理系统(鲜花1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 本网上花店微信小程序分为管理员还有用户两个权限&#xff0c;管理员可以管理用户的基本信息内容&#xff0c;可以管理公告信息以及鲜花信息&#xff0c;能够与用户进行相互交流等操作&am…

金融学期末速成笔记

【拯救者】金融学速成&#xff08;基础习题&#xff09; 重点: 市场经济是发达的商品经济。在市场经济条件下&#xff0c;市场机制作为资源配置方式&#xff0c;发挥基础性作用。 除具有商品经济的一般特征外&#xff0c;与商品经济相比&#xff0c;市场经济还具有一些新的特征…

后悔没早点知道,Coze 插件 + Cursor 原来可以这样赚钱

最近智能体定制化赛道异常火爆。 打开闲鱼搜索"Coze 定制",密密麻麻的服务报价直接刷屏,即使表明看起来几十块的商家,一细聊,都是几百到上千不等的报价。 有趣的是,这些智能体定制化服务背后,最核心的不只是工作流设计,还有一个被很多人忽视的重要角色 —— …

嵌入式采集网关(golang版本)

为了一次编写到处运行&#xff0c;使用纯GO编写&#xff0c;排除CGO&#xff0c;解决在嵌入式中交叉编译难问题 硬件设备&#xff1a;移远EC200A-CN LTE Cat 4 无线通信模块&#xff0c;搭载openwrt操作系统&#xff0c;90M内存

基于Multisim数字电子秒表0-60S电路(含仿真和报告)

【全套资料.zip】数字电子秒表电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.秒表最大计时值为60秒&#xff1b; 2. 2位数码管显示&#xff0c;分辨率为1秒&#xff1b; 3.具有清零…