Promise的魔法:揭开JavaScript异步编程的面纱

【作者主页】:小鱼神1024

【擅长领域】:JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等

Promise 是什么

Promise 是为了解决 JavaScript 中的异步编程问题而引入的一种机制。

Promise 解决了什么问题

在 JavaScript 中,许多操作都是异步的,例如网络请求、文件读写、定时器等。在传统的回调函数方式中,处理多个异步操作会导致代码嵌套层级过深,可读性差,维护困难,被称为“回调地狱”(Callback Hell)。

以下是一个简单的例子来说明回调地狱的问题。假设我们需要按顺序执行三个异步操作:获取用户信息、获取用户订单列表、获取订单详情。每个操作都需要通过回调函数来处理结果。

getUserInfo((userInfo) => {
  getOrderList(userInfo.userId, (orderList) => {
    getOrderDetails(orderList[0].orderId, (orderDetails) => {
      // 处理订单详情
    });
  });
});

在这个例子中,每个异步操作都依赖于上一个操作的结果,因此需要将后续操作的代码嵌套在回调函数中。随着操作的增多,嵌套的层级会不断增加,导致代码的缩进增多,可读性变差,而且很容易出现错误。

为了解决这个问题,Promise 提供了一种更优雅、更结构化的方式来处理异步操作。它将异步操作封装成一个 Promise 对象,可以通过链式调用的方式处理异步操作的结果和错误。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败),可以根据异步操作的结果来改变状态。Promise 对象的状态一旦改变,就不会再变化。

使用 Promise,可以将异步操作的处理逻辑从回调函数中提取出来,使代码更加清晰、易读。同时,Promise 还提供了一些方法,如 .then()、.catch() 和 .finally(),用于处理异步操作的结果和错误,使代码更加可控。

Promise 的基本用法

Promise 是一个构造函数,可以通过 new 关键字来创建一个 Promise 对象。Promise 构造函数接收一个函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们都是函数类型。resolve 函数用于将 Promise 对象的状态从 pending(进行中)变为 fulfilled(已成功),reject 函数用于将 Promise 对象的状态从 pending(进行中)变为 rejected(已失败)。

创建 Promise 对象
  • 用法
const promise = new Promise((resolve, reject) => {
  // 异步操作
  // 处理成功,调用 resolve
  // 处理失败,调用 reject
});
  • 案例
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let count = Math.random() * 10;
    if (count < 5) {
      // 当随机数小于5,promise完成了执行
      resolve(count);
    } else {
      reject("错误,count>5");
    }
    console.log("执行完成");
  }, 1000);
});
处理 Promise 成功状态的回调函数
  • 用法
promise.then((result) => {
  // 处理成功状态的逻辑
});
  • 案例
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let count = Math.random() * 10;
    if (count < 5) {
      // 当随机数小于5,promise完成了执行
      resolve(count);
    } else {
      reject("错误,count>5");
    }
    console.log("执行完成");
  }, 1000);
});

promise.then((data) => {
  console.log(data);
});

有的小伙伴,可能不太理解,promise.then() 里面的回调函数的形参参数(data),代表的具体是谁?其实,在正常执行情况下,promise.then() 里面的回调函数可以理解为是 resolve,因为 resolve 、reject 本身就是一个函数。即:promise.then(resolve)

处理 Promise 失败状态的回调函数
  • 用法
promise.catch((error) => {
  // 处理失败状态的逻辑
});
  • 案例
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let count = Math.random() * 10;
    if (count < 5) {
      // 当随机数小于5,promise完成了执行
      resolve(count);
    } else {
      reject("错误,count>5");
    }
    console.log("执行完成");
  }, 1000);
});

promise.catch((error) => {
  console.log(error);
});
处理 Promise 无论成功或失败状态的回调函数
  • 用法
promise.finally(() => {
  // 处理成功或失败状态的逻辑
});
  • 案例
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let count = Math.random() * 10;
    if (count < 5) {
      // 当随机数小于5,promise完成了执行
      resolve(count);
    } else {
      reject("错误,count>5");
    }
    console.log("执行完成");
  }, 1000);
});

promise.finally(() => {
  console.log("无论成功或失败都会执行");
});
Promise 的链式调用
  • 用法
promise
  .then((result) => {
    // 处理成功状态的逻辑
    return anotherPromise; // 返回另一个 Promise 对象
  })
  .then((result) => {
    // 处理另一个 Promise 成功状态的逻辑
  })
  .catch((error) => {
    // 处理任何一个 Promise 失败状态的逻辑
  });
  • 案例
const promise = new Promise((resolve, reject) => {
  resolve(1);
})
  .then((value) => {
    console.log("value1", value);
    return value * 10;
  })
  .then((value) => {
    console.log("value2", value);
  })
  .then((value) => {
    console.log("value3", value);
  });
并行执行多个 Promise, 返回所有 Promise 的结果
  • 用法
Promise.all([promise1, promise2, promise3])
  .then((results) => {
    // 处理所有 Promise 成功状态的逻辑,results 是一个包含所有结果的数组
  })
  .catch((error) => {
    // 处理任何一个 Promise 失败状态的逻辑
  });
  • 案例
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 1");
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 2");
  }, 1500);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 3");
  }, 1000);
});

Promise.all([promise1, promise2, promise3])
  .then((results) => {
    console.log(results); // ['Result 1', 'Result 2', 'Result 3']
    // 处理所有 Promise 成功状态的逻辑
  })
  .catch((error) => {
    console.error(error); // 如果任何一个 Promise 失败,会在这里处理错误
    // 处理任何一个 Promise 失败状态的逻辑
  });

在这个例子中,我们创建了三个 Promise 对象,每个 Promise 都会在不同的时间间隔后返回一个结果。使用 Promise.all 方法,我们将这三个 Promise 对象传递给它,并在所有 Promise 都成功完成后获取它们的结果。在 .then 方法中,我们可以处理所有 Promise 成功状态的逻辑,并在控制台打印结果数组。如果任何一个 Promise 失败,会立即触发 .catch 方法,并在控制台打印错误信息。

并行执行多个 Promise,返回最先执行完的 Promise 的结果
  • 用法
Promise.race([promise1, promise2, promise3])
  .then((result) => {
    // 处理第一个 Promise 成功状态的逻辑
  })
  .catch((error) => {
    // 处理第一个 Promise 失败状态的逻辑
  });
  • 案例
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 1");
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 2");
  }, 1500);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Result 3");
  }, 1000);
});

Promise.race([promise1, promise2, promise3])
  .then((result) => {
    console.log(result); // 'Result 3'
    // 处理第一个 Promise 成功状态的逻辑
  })
  .catch((error) => {
    console.error(error); // 如果第一个 Promise 失败,会在这里处理错误
    // 处理第一个 Promise 失败状态的逻辑
  });
Promise.resolve(value)

在实际开发过程中,有时需要将现有对象转为 Promise 对象,Promise.resolve 方法就起到这个作用

new Promise((resolve) => resolve("Hello World"));
// 等价于
Promise.resolve("Hello World");

静态方法 Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式。

创建 Promise 异步函数
const getShopDetailById = (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟后端请求
      let count = Math.random() * 10;
      if (id && count < 5) {
        // 当随机数小于5,假设后端接口正常返回数据
        resolve({ code: 0, msg: "ok" });
      } else {
        reject("接口异常");
      }
    }, 1000);
  });
};

// 假如商品的主键id为:1
getShopDetailById("1").then((data) => {
  console.log(data);
});

创建异步函数还有更好的方案,那就是 async/await。继续看下一篇吧。

创作不易,动动您发财的小手,点赞关注一波,支持我创作更多对您有帮助的文章!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三元前驱体废水回收镍钴工艺:环保与经济效益的双重胜利

在全球新能源产业迅猛发展的背景下&#xff0c;锂离子电池作为绿色能源的核心组件&#xff0c;其需求量激增&#xff0c;带动了上游材料市场&#xff0c;尤其是三元前驱体材料的蓬勃发展。然而&#xff0c;伴随着行业的快速扩张&#xff0c;三元前驱体生产过程中产生的含镍钴废…

嘉绩咨询低成本连锁品牌招商全案陪跑赋能中小品牌有效招商

以企业战略导航为基石&#xff0c;致力于构建全面招商生态系统的嘉绩咨询&#xff0c;今天宣布推出面向中小品牌的低成本连锁招商全案陪跑服务。这项创新服务是为了帮助具有潜力的中小品牌在市场中迅速构建渠道&#xff0c;通过有效招商策略促进成长。 嘉绩咨询凭借先进的“教育…

PCI认证HSM的特点

PCI认证HSM(硬件安全模块)在支付卡行业中扮演着至关重要的角色&#xff0c;它是确保支付交易数据完整性和机密性的关键组件。以下是关于PCI认证HSM的详细介绍&#xff1a; 一、PCI认证HSM的定义 PCI认证HSM是专门用于支付行业的硬件安全模块&#xff0c;它满足支付卡行业(PCI)的…

【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统

最终效果 文章目录 最终效果前言素材下载图片配置获取格子坐标动态控制背包大小添加物品移动物品物品跟随鼠标创建物品的容器&#xff0c;定义不同物品修改物品尺寸修复物品放置位置问题按物品尺寸占用对应大小的格子判断物品是否超出边界范围物品放置重叠&#xff0c;交换物品…

Oracle优化案例-教你在线搞定top cpu的sql(十二)

监控告警阈值load 大于10 SQL如下&#xff0c;太好用了 SELECT A.SQL_ID, A.SESS_COUNT, A.CPU_LOAD, B.SQL_TEXTFROM (SELECT SQL_ID,COUNT(*) SESS_COUNT,ROUND(COUNT(*) / SUM(COUNT(*)) OVER(), 2) CPU_LOADFROM V$ACTIVE_SESSION_HISTORYWHERE SAMPLE_TIME > SYSDATE…

[深度学习] 门控循环单元GRU

门控循环单元&#xff08;Gated Recurrent Unit, GRU&#xff09;是一种用于处理序列数据的递归神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;变体&#xff0c;它通过引入门控机制来解决传统RNN在处理长序列时的梯度消失问题。GRU与长短期记忆网络&#xff0…

反射及动态代理

反射 定义&#xff1a; 反射允许对封装类的字段&#xff0c;方法和构造 函数的信息进行编程访问 图来自黑马程序员 获取class对象的三种方式&#xff1a; 1&#xff09;Class.forName("全类名") 2&#xff09;类名.class 3) 对象.getClass() 图来自黑马程序员 pac…

前端JS必用工具【js-tool-big-box】学习,数值型数组的正向排序和倒向排序

这一小节&#xff0c;我们说一下前端 js-tool-big-box 这个工具库&#xff0c;添加的数值型数组的正向排序和倒向排序。 以前呢&#xff0c;我们的数组需要排序的时候&#xff0c;都是在项目的utils目录里&#xff0c;写一段公共方法&#xff0c;弄个冒泡排序啦&#xff0c;弄…

JNI详解

JNI简介 Java是跨平台的语言&#xff0c;但在有的时候仍需要调用本地代码&#xff08;这些代码通常由C/C编写的&#xff09;。 Sun公司提供的JNI是Java平台的一个功能强大的接口&#xff0c;JNI接口提供了Java与操作系统本地代码互相调用的功能。 Java调C 1&#xff09;使用…

Spring Boot 学习第八天:AOP代理机制对性能的影响

1 概述 在讨论动态代理机制时&#xff0c;一个不可避免的话题是性能。无论采用JDK动态代理还是CGLIB动态代理&#xff0c;本质上都是在原有目标对象上进行了封装和转换&#xff0c;这个过程需要消耗资源和性能。而JDK和CGLIB动态代理的内部实现过程本身也存在很大差异。下面将讨…

VMware vSphere 8.0 Update 3 发布下载 - 企业级工作负载平台

VMware vSphere 8.0 Update 3 发布下载 - 企业级工作负载平台 vSphere 8.0U3 | ESXi 8.0U3 & vCenter Server 8.0U3 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-vsphere-8-u3/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&am…

Java面试八股之JVM内存溢出的原因及解决方案

JVM内存溢出的原因及解决方案 JVM内存溢出&#xff08;Out Of Memory&#xff0c;OOM&#xff09;通常是由于程序运行过程中内存使用不当造成的&#xff0c;常见原因及相应的解决方案如下&#xff1a; 原因及解决方案 内存中加载的数据量过大 原因&#xff1a;一次性从数据…

运维入门技术——监控的三个维度(非常详细)零基础收藏这一篇就够了_监控维度怎么区分

一个好的监控系统最后要做到的形态:实现Metrics、Tracing、Logging的融合。监控的三个维度也就是Metrics、Tracing、Logging。 Metrics Metrics也就是我们常说的指标。 首先它的典型特征就是可聚合(aggregatable).什么是可聚合的呢,简单讲可聚合就是一种基本单位可以在一种维…

Verilog刷题笔记48——FSM1型异步复位

题目: 解题&#xff1a; module top_module(input clk,input areset, // Asynchronous reset to state Binput in,output out);// parameter A0, B1; reg state, next_state;always (*) begin // This is a combinational always block// State transition logiccase(…

加拿大魁北克IT人士的就业分析

魁北克省作为加拿大东部的一个重要省份&#xff0c;近年来在IT行业的就业市场上展现出了强劲的增长势头。随着数字化转型的加速&#xff0c;魁北克对IT专业人士的需求日益增加&#xff0c;特别是在软件开发、网络安全、数据分析和人工智能等领域。 热门职位方面&#xff0c;软…

禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》Chapter 9插图

禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 Chapter 9插图

201.回溯算法:全排列(力扣)

class Solution { public:vector<int> res; // 用于存储当前排列组合vector<vector<int>> result; // 用于存储所有的排列组合void backtracing(vector<int>& nums, vector<bool>& used) {// 如果当前排列组合的长度等于 nums 的长度&am…

用 Rust 实现一个替代 WebSocket 的协议

很久之前我就对websocket颇有微词&#xff0c;它的确满足了很多情境下的需求&#xff0c;但是仍然有不少问题。对我来说&#xff0c;最大的一个问题是websocket的数据是明文传输的&#xff0c;这使得websocket的数据很容易遭到劫持和攻击。同时&#xff0c;WebSocket继承自HTTP…

【操作系统】操作系统发展简史

目录 1.前言 2.第一代&#xff08;1945~1955&#xff09;&#xff1a;真空管和穿孔卡片 3.第二代&#xff08;1955~1965&#xff09;&#xff1a;晶体管和批处理系统 4.第三代&#xff08;1965~1980&#xff09;&#xff1a;集成电路和多道程序设计 5.第四代&#xff08;1…

关于VMware遇到的一些问题

问题一&#xff1a;打不开磁盘…或它所依赖的某个快照磁盘&#xff0c;开启模块DiskEarly的操作失败&#xff0c;未能启动虚拟机 解决方法&#xff1a; 首先将centos 7关机&#xff0c;然后把快照1删掉 然后打开虚拟机所在目录&#xff0c;把提示的000001.vmdk全部删除&…