深入理解 JavaScript Promise

引言

在当今的 JavaScript 开发中,异步编程已经成为了标准实践。随着我们的应用程序变得越来越复杂,管理异步操作和回调变得尤为关键。在过去,开发者们常常发现自己陷入所谓的“回调地狱”,这是一种由于深层嵌套的回调函数导致的难以维护的代码结构。为了解决这一问题,Promise 应运而生,并迅速成为了现代 JavaScript 编程中不可或缺的一部分。

Promise 在异步编程中扮演着至关重要的角色。它提供了一种更加清晰和可控的方式来处理异步操作,使得代码更加模块化和易于理解。通过 Promise,我们可以将异步操作的结果和处理逻辑分离,避免回调地狱,并实现更加优雅的代码流控制。

本文的目的是帮助读者深入理解 Promise 的工作原理,掌握其核心方法和特性,并探讨最佳实践。无论您是初学者还是有一定经验的开发者,理解 Promise 都将对您的 JavaScript 编程技能提升大有裨益。我们将从 Promise 的基本概念开始,逐步深入到更高级的应用,让您能够自信地使用 Promise 来构建稳健和高效的异步应用程序。

Promise 基础

Promise 的定义和基本概念
  • Promise 是一个表示异步操作最终完成或失败的对象。
  • 它代表了某个未来才会知道结果的事件(通常是一个异步操作)。
  • Promise 是一个包含三种状态的对象:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
Promise 的三种状态
  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
Promise 的语法结构
  • Promise 是一个构造函数,接受一个执行器函数(executor function)作为参数。
  • 执行器函数接受两个参数:resolve 和 reject,它们分别是用来解决和拒绝 Promise 的函数。
创建一个 Promise 
const myPromise = new Promise((resolve, reject) => {// 异步操作const success = true; // 假设这是异步操作的结果if (success) {resolve('Operation succeeded');} else {reject('Operation failed');}
});
Promise 的链式调用
  • Promise 对象的 then 方法返回一个新的 Promise,可以链式调用。
  • then 方法接受两个可选参数:onFulfilled 和 onRejected。
  • catch 方法是 then(null, onRejected) 的语法糖。
myPromise.then((value) => {console.log(value); // 'Operation succeeded'return value; // 返回值将传给下一个 then}).then((value) => {console.log(`Chain continues with ${value}`);}).catch((error) => {console.log(error); // 'Operation failed'});
Promise 的错误处理:catch 方法
  • catch 方法用于处理 Promise 中的错误。
  • 如果在链式调用的任何阶段发生错误,都会被 catch 捕获。
myPromise.then((value) => {console.log(value);throw new Error('An error occurred'); // 模拟错误}).catch((error) => {console.log(error.message); // 'An error occurred'});

Promise 方法详解

then 方法
  • then 方法用于注册当 Promise 解决时应调用的回调函数。
  • 它可以接受两个参数:onFulfilled 和 onRejected。
  • then 返回一个新的 Promise,允许链式调用。
promise.then(onFulfilled, onRejected);
catch 方法
  • catch 方法用于注册当 Promise 被拒绝时应调用的回调函数。
  • 它是 then(null, onRejected) 的语法糖。
  • catch 也可以返回一个新的 Promise,继续链式调用。
promise.catch(onRejected);
finally 方法
  • finally 方法用于注册一个不管 Promise 最终状态如何都会执行的回调函数。
  • 它不接受任何参数,表示不关心 Promise 的结果。
  • finally 不会改变 Promise 的状态。
promise.finally(onFinally);
Promise.resolve
  • Promise.resolve 方法返回一个被解决的 Promise。
  • 如果传入的参数是一个 Promise,它会直接返回该 Promise。
  • 如果传入的是非 Promise 值,它会返回一个以该值为解决的 Promise。
Promise.resolve(value);
Promise.reject
  • Promise.reject 方法返回一个被拒绝的 Promise。
  • 它的行为与 Promise.resolve 类似,但是返回的是被拒绝的 Promise。
Promise.reject(reason);
Promise.all
  • Promise.all 方法用于并行执行多个 Promise。
  • 它接受一个 Promise 数组作为输入。
  • 只有当所有的 Promise 都解决时,返回的 Promise 才会解决。
  • 如果有一个 Promise 被拒绝,返回的 Promise 会立即被拒绝。
Promise.all([promise1, promise2, promise3]);
Promise.race
  • Promise.race 方法同样接受一个 Promise 数组作为输入。
  • 它返回一个 Promise,它在任意一个输入的 Promise 解决或拒绝后立即解决或拒绝。
Promise.race([promise1, promise2, promise3]);
Promise.allSettled
  • Promise.allSettled 方法用于等待所有传入的 Promise 被解决或拒绝。
  • 它返回一个 Promise,当所有输入的 Promise 都被解决或拒绝时,这个 Promise 会解决。
  • 返回的每个结果都是一个对象,包含状态(fulfilled 或 rejected)和相应的值或原因。
Promise.allSettled([promise1, promise2, promise3]);

易混淆点

Promise.allPromise.allSettled 都是JavaScript中用于处理多个Promise对象的函数,但它们的行为和返回结果有所不同。

Promise.all

Promise.all 接受一个Promise对象的数组作为参数。当调用这个函数时,它将等待数组中的所有Promise都解决(fulfilled)或至少有一个被拒绝(rejected)。以下是Promise.all的一些特点:

  • 如果数组中的所有Promise都成功解决,Promise.all 返回一个新的Promise,该Promise解决时的值是一个数组,包含所有输入Promise解决时的值,按照它们在数组中的顺序排列。
  • 如果数组中的任何一个Promise被拒绝,Promise.all 返回的Promise将立即被拒绝,其拒绝的原因与第一个被拒绝的Promise的原因相同。这意味着,一旦有一个Promise失败,Promise.all 将忽略其他Promise的结果。

示例代码:

const promise1 = Promise.resolve(3);
const promise2 = Promise.resolve(42);
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 100, 'error'));Promise.all([promise1, promise2, promise3]).then(values => {console.log(values); // 输出:[3, 42, 'error'],因为promise3被拒绝}).catch(error => {console.error(error); // 输出:'error'});

Promise.allSettled

Promise.allSettled 也接受一个Promise对象的数组作为参数,但它的行为与Promise.all有所不同:

  • 无论数组中的Promise是解决还是被拒绝,Promise.allSettled 都会等待所有Promise都完成(settled),然后解决。
  • Promise.allSettled 返回的Promise解决时的值是一个数组,其中每个元素是一个对象,描述了每个Promise的结果。每个对象包含两个属性:status 和 value 或 reason。如果Promise解决,status 是 'fulfilled'value 是解决的值;如果Promise被拒绝,status 是 'rejected'reason 是拒绝的原因。

示例代码:

const promise1 = Promise.resolve(3);
const promise2 = Promise.resolve(42);
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 100, 'error'));Promise.allSettled([promise1, promise2, promise3]).then(results => {console.log(results);// 输出:// [//   { status: 'fulfilled', value: 3 },//   { status: 'fulfilled', value: 42 },//   { status: 'rejected', reason: 'error' }// ]});

总结:

  • Promise.all 适合于你需要所有Promise都成功完成的场景,并且任何一个Promise的失败都会中断整个操作。
  • Promise.allSettled 适合于你需要知道所有Promise的结果,无论它们是成功还是失败的场景。

 

 下面是完整的代码实现:

// then 方法示例:处理 Promise 解决后的结果
const promiseThen = new Promise((resolve, reject) => {setTimeout(() => {resolve('Success'); // 异步操作成功,解决 Promise}, 1000);
});promiseThen.then(value => {console.log(value); // 'Success',输出解决后的值return value + ' with then'; // 返回修改后的值,链式调用}).then(value => {console.log(value); // 'Success with then',输出上一步返回的值}).catch(error => {console.log(error); // 如果有任何一步出错,这里会捕获错误});// catch 方法示例:捕获 Promise 被拒绝时的错误
const promiseCatch = new Promise((resolve, reject) => {setTimeout(() => {reject('Error'); // 异步操作失败,拒绝 Promise}, 1000);
});promiseCatch.then(value => {console.log(value);}).catch(error => {console.log(error); // 'Error',输出拒绝的原因});// finally 方法示例:无论 Promise 成功或失败都会执行
const promiseFinally = new Promise((resolve, reject) => {setTimeout(() => {resolve('Resolved'); // 异步操作成功,解决 Promise}, 1000);
});promiseFinally.then(value => {console.log(value); // 'Resolved',输出解决后的值}).finally(() => {console.log('Finally called'); // 无论成功或失败,这里都会执行});// Promise.resolve 示例:快速创建一个已解决的 Promise
const promiseResolve = Promise.resolve('Resolved immediately');
promiseResolve.then(value => {console.log(value); // 'Resolved immediately',直接输出已解决的值
});// Promise.reject 示例:快速创建一个已拒绝的 Promise
const promiseReject = Promise.reject('Rejected immediately');
promiseReject.catch(reason => {console.log(reason); // 'Rejected immediately',输出拒绝的原因
});// Promise.all 示例:并行执行多个 Promise,所有成功后返回结果数组
const promiseAll1 = new Promise(resolve => {setTimeout(() => {resolve('Promise 1'); // 第一个异步操作成功}, 1000);
});
const promiseAll2 = new Promise(resolve => {setTimeout(() => {resolve('Promise 2'); // 第二个异步操作成功}, 2000);
});Promise.all([promiseAll1, promiseAll2]).then(values => {console.log(values); // ['Promise 1', 'Promise 2'],输出所有 Promise 解决后的值}).catch(error => {console.log(error); // 如果有任何 Promise 被拒绝,这里会捕获错误});// Promise.race 示例:多个 Promise 竞争,返回第一个解决或拒绝的 Promise
const promiseRace1 = new Promise(resolve => {setTimeout(() => {resolve('Promise 1'); // 第一个异步操作成功}, 1000);
});
const promiseRace2 = new Promise(resolve => {setTimeout(() => {resolve('Promise 2'); // 第二个异步操作成功}, 500);
});Promise.race([promiseRace1, promiseRace2]).then(value => {console.log(value); // 'Promise 2',因为它是第一个解决的}).catch(error => {console.log(error); // 如果有任何 Promise 被拒绝,这里会捕获错误});// Promise.allSettled 示例:等待所有 Promise 解决或拒绝,返回每个 Promise 的结果
const promiseAllSettled1 = new Promise(resolve => {setTimeout(() => {resolve('Promise 1'); // 第一个异步操作成功}, 1000);
});
const promiseAllSettled2 = new Promise((resolve, reject) => {setTimeout(() => {reject('Promise 2'); // 第二个异步操作失败}, 500);
});Promise.allSettled([promiseAllSettled1, promiseAllSettled2]).then(results => {results.forEach(result => {if (result.status === 'fulfilled') {console.log(result.value); // 'Promise 1',输出解决后的值} else {console.log(result.reason); // 'Promise 2',输出拒绝的原因}});});

Promise 的异步行为

Promise 的异步特性
  • Promise 是异步编程的一种解决方案,它允许我们在异步操作完成时获取其结果。
  • Promise 本身并不会立即执行,而是会在当前的同步代码执行完毕后,在下一个事件循环周期中执行。
微任务(microtask)和宏任务(macrotask)
  • JavaScript 运行时环境将任务分为微任务和宏任务。
  • 宏任务包括脚本执行、定时器回调(如 setTimeout 和 setInterval)、IO 操作等。
  • 微任务包括 Promise 的回调、异步 DOM 操作的回调、process.nextTick(Node.js 环境)等。
  • 微任务的执行时机早于宏任务,且在同一个事件循环周期中,微任务队列会优先清空。
Promise 在事件循环中的执行顺序
  • 当一个 Promise 被解决或拒绝时,其回调函数(thencatchfinally)会被添加到微任务队列中。
  • 如果当前执行栈为空,微任务队列会在下一个事件循环的开始被清空。
  • 这意味着 Promise 的回调函数会在所有同步代码执行完毕后,但在任何新的宏任务开始之前执行。

下面是一个代码示例,展示了 Promise 在事件循环中的执行顺序:

console.log('Script start');// 微任务示例
Promise.resolve().then(() => {console.log('Microtask 1');
});// 宏任务示例
setTimeout(() => {console.log('Macrotask 1');
}, 0);console.log('Script end');// 执行顺序:
// Script start
// Script end
// Microtask 1
// Macrotask 1

在这个示例中,尽管 setTimeout 回调被注册在 Promise.resolve().then() 之前,但由于 Promise 回调是微任务,而 setTimeout 回调是宏任务,所以 Promise 的回调会先于 setTimeout 的回调执行。

相关资料推荐

Promise 和异步编程

  • MDN Web Docs - Promise:MDN 提供了关于 Promise 的详细文档,包括基本概念、用法和示例。链接
  • JavaScript.info - Promises:JavaScript.info 提供了关于 Promise 的详细介绍,包括异步编程的基础知识。链接

JavaScript 事件循环和异步模型

  • MDN Web Docs - Event Loop:MDN 上的事件循环文档,解释了 JavaScript 的事件循环机制。链接
  • Philip Roberts - What the heck is the event loop anyway?:Philip Roberts 在 JSConf EU 2014 上的演讲,对事件循环进行了生动的解释。链接

微任务和宏任务

  • JavaScript Visualized: Microtasks and Macrotasks:一个可视化的解释,帮助理解微任务和宏任务在 JavaScript 中的工作方式。链接
  • Understanding JavaScript’s Event Loop and Call Stack:一个关于 JavaScript 事件循环和调用栈的深入解析。链接

书籍推荐

  • 《You Don’t Know JS》:这是一本深入探讨 JavaScript 语言核心机制的书籍,其中有一章专门讨论异步编程和 Promise。链接
  • 《JavaScript: The Good Parts》:虽然这本书不专注于异步编程,但它提供了 JavaScript 语言的基础知识,有助于更好地理解异步编程的概念。链接

总结

Promise 作为现代 JavaScript 异步编程的核心,为开发者提供了一种清晰、可控的方式来管理复杂的异步流程。通过理解 Promise 的基本概念、状态、链式调用以及错误处理,我们可以编写出更加模块化、易于维护的代码。

本文详细介绍了 Promise 的各种方法和特性,包括如何创建和使用 Promise,以及如何处理异步操作的成功和失败。我们还探讨了 Promise 的异步行为,包括微任务和宏任务的概念,以及 Promise 在事件循环中的执行顺序。

通过掌握这些知识,您可以更加自信地使用 Promise 来构建稳健和高效的异步应用程序。无论是处理单个 Promise,还是管理多个 Promise,Promise 都能为您提供强大的支持。

最后,感谢您阅读这篇文章。希望您能够从中学到有用的知识和技巧,并在您的 JavaScript 项目中充分利用 Promise 的强大功能。

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

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

相关文章

三人同行乐享模式:社交电商的新趋势

在数字化时代,社交电商正以其独特的优势崭露头角。其中,“三人同行乐享模式”就是一种创新的购物激励机制,它通过消费者的社交互动和分享,不仅促进了产品的销售,更加强了品牌的推广和影响力。 一、模式简介 此模式的核…

RockChip Android12 Settings二级菜单

一:概述 本文将针对Android12 Settings的二级菜单System进行说明。 二:System 1、Activity packages/apps/Settings/AndroidManifest.xml <activityandroid:name=".Settings$SystemDashboardActivity"android:label="@string/header_category_system&quo…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-30Kaggle竞赛:图片分类

30Kaggle竞赛&#xff1a;图片分类 比赛链接&#xff1a; https://www.kaggle.com/c/classify-leaves 导入包 import torch import torchvision from torch.utils.data import Dataset, DataLoader from torchvision import transforms import numpy as np import pandas as…

【消息队列】六万字长文详细带你RabbitMQ从入门到精通

目录 1、基础篇1.1 为什么要用消息队列MQ1.2 什么是消息队列&#xff1f;1.3 RabbitMQ体系结构介绍1.4 RabbitMQ安装1.5 Hello World1.5.1 目标1.5.2 具体操作 1.6 RabbitMQ用法1.6.1 Work Queues1.6.2 Publish/Subscribe1.6.3 Routing1.6.4 Topics1.6.5 工作模式小结 2. 进阶篇…

推荐三款必备软件,个个五星好评,你一定不要错过

WiseCare365 WiseCare365是一款由WiseCleaner推出的综合性Windows系统优化和加速工具。它集成了多种功能&#xff0c;旨在帮助用户清理、优化和维护电脑系统&#xff0c;提升电脑性能和安全性。 WiseCare365的主要功能包括&#xff1a; 系统清理&#xff1a;它可以清理各种缓存…

Java.io包:从基础到高级的全面指南

Java.io包是Java编程语言中用于处理输入和输出的核心库之一。它提供了一系列类和接口&#xff0c;用于与文件、流、输入输出等进行交互。Java.io的设计目的是为了提供一个简单且一致的API&#xff0c;使得开发者能够轻松地处理各种输入输出操作&#xff0c;无论是文件读写、网络…

CSC公派|哲学老师赴英国红砖大学访学交流

T老师申报CSC公派访问学者&#xff0c;要求世界排名Top200的英国大学。我们在一个月内先后获得了利物浦大学和兰卡斯特大学的邀请函&#xff0c;这两所高校均位列Top200。最终T老师选择英国红砖高校之一的利物浦大学并申报成功顺利出国。 T老师背景&#xff1a; 申请类型&…

AutoTokenizer.from_pretrained报错TypeError: expected string or bytes-like object

问题 tokenizer AutoTokenizer.from_pretrained(distilbert-base-uncased)在用HuggingFace的Transformers加载分词器的时候报错&#xff0c;代码如下: > load tokenizer model distilbert Traceback (most recent call last):File "E:\PythonProjects\Sentiment_Ana…

Vue父组件mounted执行完后再执行子组件mounted

// 创建地图实例 this.map new BMap.Map(‘map’) } } ... 现在这样可能会报错&#xff0c;因为父组件中的 map 还没创建成功。必须确保父组件的 map 创建完成&#xff0c;才能使用 this.$parent.map 的方法。 那么&#xff0c;现在的问题是&#xff1a;如何保证父组件 mo…

端到端的全人体关键点检测:手把手实现从YOLOPose到YOLOWhole

目录 一、搭建yolopose平台二、迁移训练任务2.1 任务拓展数据准备训练模型测试训练模型结论To-do list: 1、数据集,COCO-whole, Halpe;下载好; 2、模型搭建,先基于yolov8来检测人体姿态,17个点; 3、迁移任务,17个点,把它拓展到133个点; 4、优化133个点的模型; 一、搭…

深入理解RLHF技术

在《LLM对齐“3H原则”》这篇文章中&#xff0c;我们介绍了LLM与人类对齐的“3H”原则&#xff0c;但是这些对齐标准主要是基于人类认知进行设计的&#xff0c;具有一定的主观性。因此&#xff0c;直接通过优化目标来建模这些对齐标准较为困难。本文将介绍基于人类反馈的强化学…

ONLYOFFICE 8.1:全面升级,PDF编辑与本地化加强版

目录 &#x1f4d8; 前言 &#x1f4df; 一、什么是 ONLYOFFICE 桌面编辑器&#xff1f; &#x1f4df; 二、ONLYOFFICE 8.1版本新增了那些特别的实用模块&#xff1f; 2.1. 轻松编辑器 PDF 文件 2.2. 用幻灯片版式快速修改幻灯片 2.3. 无缝切换文档编辑、审阅和查…

RS-485和RS-422通信的3.3V低功耗收发器MAX3483

描述 国产MAX3485外观和丝印 该MAX3483ESA为15kV ESD保护、3.3V、低功耗收发器&#xff0c;用于RS-485和RS-422通信。 每个设备包含一个驱动器和一个接收器。 该MAX3483ESA具有压摆率限制驱动器&#xff0c;可最大限度地降低 EMI 并减少因端接不当电缆引起的反射&#xff0c;从…

system verilog 学习1

1 数据类型 sv 引进了一些新的数据类型&#xff0c;它们具有以下优点 &#xff08;1&#xff09;双状态数据类型&#xff1a;更好的性能&#xff0c;更低的内存消耗 &#xff08;2&#xff09;队列、动态和关联数组&#xff1a;减少内存消耗&#xff0c;自带搜索和分类功能 &a…

C# 使用Vector256写了一个简单的帮助类Vector256Helper

当数据量大的时候用普通代码计算非常耗时&#xff0c;这里简单利用simd加速处理 internal unsafe class Vector256Helper{/// <summary>/// 统计元素个数/// </summary>/// <param name"array"></param>/// <param name"elementToCo…

板凳--------57.Linux/Unix 系统编程手册(下) -- SOCKET : Unix domain

https://blog.51cto.com/u_15567199/5204540 【linux网络编程】容错处理文件 wrap.h、wrap.c_wx623c6c9. // 容错处理 wrap.h #ifndef _WRAP_H_ #define _WRAP_H_#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <error.h> #i…

【BSCP系列第2期】XSS攻击的深度剖析和利用(文末送书)

文章目录 前言一、官方地址二、开始&#xff08;15个&#xff09;1&#xff1a;Lab: DOM XSS in document.write sink using source location.search inside a select element2&#xff1a;Lab: DOM XSS in AngularJS expression with angle brackets and double quotes HTML-e…

北邮《计算机网络》MAC子层笔记

文章目录 缩写复习MAC层所在层次动态分配信道算法们的简要介绍信道的五条基本假设多路访问的协议&#xff08;理论上的协议&#xff09;aloha协议CSMA协议其他冲突避免协议无线局域网协议 &#xff0c;MACA 以太网协议802.3&#xff08;实际协议&#xff0c;刚刚是理论&#xf…

小白学python(第一天)

在有了C语言的基础后&#xff0c;我们学python会变得相当容易&#xff0c;毕竟c生万物&#xff0c;废话不多说&#xff0c;直接进入我们的正题 课前准备 Python环境的搭建以及Pycharm的安装 python环境安装 Download Python | Python.org 因为我的电脑是windows&#xff0c;…

C++精解【6】

文章目录 eigenMatrix基础例编译时固定尺寸运行指定大小 OpenCV概述 eigen Matrix 基础 所有矩阵和向量都是Matrix模板类的对象。向量也是矩阵&#xff0c;单行或单列。Matrix模板类6个参数&#xff0c;常用就3个参数&#xff0c;其它3个参数有默认值。 Matrix<typename…