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

147.《手写实现 Promise.all 与 Promise.race》

文章目录

  • 手写实现 `Promise.all` 与 `Promise.race`
    • 引言
    • 实现 `myPromiseAll`:等待所有任务完成
      • 核心需求分析
      • 实现思路
      • 代码实现
      • 测试验证
    • 实现 `myPromiseRace`:谁先完成就用谁
      • 核心需求分析
      • 实现思路
      • 代码实现
      • 测试验证
    • 设计思想对比
      • 🌰 典型应用场景
    • 总结
    • 上班心得

手写实现 Promise.allPromise.race

摘要: Promise.allPromise.race 是 JavaScript 中处理多个异步任务的常用方法。它们分别代表了“与”和“或”的并发控制逻辑。为了真正理解其内部工作机制,本文将从零开始,手写实现这两个方法。通过代码实践,深入剖析其设计思想与核心原理。


引言

在现代前端开发中,异步编程无处不在。我们经常需要同时发起多个网络请求,例如获取用户信息、商品列表和页面配置。如何高效地处理这些并发请求?

  • Promise.all:等待所有异步操作完成,才进行下一步。适用于“全部成功才算成功”的场景。
  • Promise.race:只要任意一个异步操作完成,就立即响应。适用于“谁先完成就用谁”的竞速场景。

虽然这两个方法使用简单,但它们的内部实现却蕴含着精妙的异步控制逻辑。本文将通过手写代码,带你一步步实现 myPromiseAllmyPromiseRace,揭开它们的神秘面纱。


实现 myPromiseAll:等待所有任务完成

核心需求分析

  • 接收一个 Promise 数组。
  • 所有 Promise 都 resolve 时,返回一个包含所有结果的数组。
  • 任一 Promise reject 时,立即返回该错误(短路机制)。
  • 结果数组的顺序应与输入数组一致。

实现思路

  1. 输入校验: 检查参数是否为数组且非空。
  2. 结果收集: 使用一个数组 result 存储每个 Promise 的返回值。
  3. 完成计数: 由于异步执行,无法通过同步循环判断是否全部完成。因此,引入计数器 count,每成功一个,计数器加一。
  4. 触发最终 resolve:count 等于数组长度时,调用 resolve(result)
  5. 错误处理: 任一 Promise 失败,立即调用 reject(err),中断整个流程。

代码实现

function myPromiseAll(data) {// 1. 输入校验:必须是数组且非空if (!Array.isArray(data) || data.length === 0) {return Promise.resolve([]);}let result = []; // 存储每个 Promise 的结果let count = 0;   // 记录已完成的 Promise 数量return new Promise((resolve, reject) => {// 遍历所有 Promisedata.forEach(prom => {// 使用 Promise.resolve 包装,确保每个元素都是 PromisePromise.resolve(prom).then(res => {result.push(res); // 收集结果count++; // 计数器加一// 当所有任务完成时,resolve 最终结果if (count === data.length) {resolve(result);}}).catch(err => {// 任一任务失败,立即 reject(短路机制)reject(err);});});});
}

测试验证

// 模拟不同耗时的 Promise
const p1 = new Promise(resolve => setTimeout(() => resolve(1), 100));
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 500));
const p3 = new Promise(resolve => setTimeout(() => resolve(3), 2000));// 测试成功情况
myPromiseAll([p1, p2, p3]).then(res => console.log('myPromiseAll res:', res)) // 输出: [1, 2, 3]// 测试失败情况(短路)
const pFail = new Promise((_, reject) => setTimeout(() => reject('出错了!'), 50));myPromiseAll([p1, pFail, p2]).catch(err => console.log('myPromiseAll err:', err)) // 输出: "出错了!",且 p2 不会再执行

实现 myPromiseRace:谁先完成就用谁

核心需求分析

  • 接收一个 Promise 数组。
  • 只要有一个 Promise 完成(resolvereject),就立即返回其结果。
  • 其余 Promise 的结果将被忽略。

实现思路

Promise.race 的实现相对简单:

  1. 并发执行: 让所有 Promise 同时开始。
  2. “先到先得”: 第一个调用 resolvereject 的 Promise,决定了最终结果。
  3. 立即返回: 一旦有结果,后续结果不再处理。

代码实现

function myPromiseRace(data) {// 输入校验if (!Array.isArray(data) || data.length === 0) {return Promise.resolve([]);}return new Promise((resolve, reject) => {// 遍历所有 Promise,让它们并发执行data.forEach(prom => {Promise.resolve(prom).then(res => {// 第一个成功的结果,立即 resolveresolve(res);}).catch(err => {// 第一个失败的结果,立即 rejectreject(err);});});});
}

测试验证

// 测试成功竞速
myPromiseRace([p1, p2, p3]).then(res => console.log('myPromiseRace res:', res)) // 输出: 1(p1 耗时最短)// 测试失败竞速
myPromiseRace([pFail, p1, p2]).catch(err => console.log('myPromiseRace err:', err)) // 输出: "出错了!"(pFail 最先 reject)

设计思想对比

特性Promise.allPromise.race
逻辑关系与(AND)或(OR)
成功条件所有 Promise 成功任意一个 Promise 成功
失败条件任意一个 Promise 失败任意一个 Promise 失败
结果所有结果的数组第一个完成的结果
典型场景批量数据获取超时控制、资源竞速

🌰 典型应用场景

  • Promise.all

    const [user, posts, config] = await Promise.all([fetch('/user'),fetch('/posts'),fetch('/config')
    ]);
    // 三者都获取成功后,再渲染页面
    
  • Promise.race(超时控制):

    function fetchWithTimeout(url, timeout) {const timer = new Promise((_, reject) => setTimeout(() => reject(new Error('Request Timeout')), timeout));return Promise.race([fetch(url), timer]);
    }
    // 请求与定时器“赛跑”,实现超时机制
    

总结

通过手写实现 myPromiseAllmyPromiseRace,我们可以清晰地看到:

  • Promise.all 的核心是“计数器 + 短路”:通过计数器判断是否全部完成,并在任一失败时立即中断。
  • Promise.race 的核心是“竞速”:所有任务并发执行,第一个返回结果的胜出。

这两个方法虽然 API 简单,但背后的设计思想非常精妙。理解其实现原理,不仅能加深对 Promise 的掌握,还能在实际开发中更灵活地运用异步控制策略。

上班心得

————————————————
上班真的好开心,需求bug来不停。

产品测试来回找,后端交互想上刑。

会议不停满楼跑,文档笔记要分清。

摸鱼睡觉一时爽,提测发布胆惊心。

大佬思绪跟得上,想法创意实践灵。

日常积累很重要,自我提高才算行。

暮然回首学生时,半载已过还未明。

处处少年何模样?如今胡须满颔停!
————————————————

http://www.dtcms.com/a/469967.html

相关文章:

  • 【HarmonyOS】异步并发和多线程并发
  • 使用docker 安装dragonfly带配置文件(x86和arm)版本
  • 企业信息型网站有哪些网站建设塞西
  • 怎么看网站是什么程序做的益阳网络
  • SpringBoot通过配置类替换配置文件配置
  • 使用Customplot绘制时间-数据曲线
  • **量子算法:探索未来的发散创新之路**随着信息技术的飞速发展,量子计算作为
  • 4. 手写数字识别,推理,批处理
  • AI编程时代的文档困境与破局之道:从Cursor到完整开发体系
  • DVWA靶场之十八:API 安全(API Security)
  • ORB_SLAM2原理及代码解析:Optimizer::LocalBundleAdjustment
  • 中文wordpress站点wordpress 获取路径
  • 从零搭建 Kubernetes 1.28 高可用集群
  • 网站建设有什么岗位职责唐山广告设计制作公司
  • Apache Doris 内部数据裁剪与过滤机制的实现原理 | Deep Dive
  • 长沙百度网站建设专精特新中小企业
  • 网站上广告wordpress导出文章word
  • Voron Trident 三叉戟 组装日记
  • 南昌公司做网站网站建设湖南岚鸿建设
  • “零成本自由派”与“钉钉生态派”:斑斑与氚云的选择
  • Flutter 仿网易云音乐播放器:唱片旋转 + 歌词滚动实现记录
  • 编写Python脚本在域名过期10天内将域名信息发送到钉钉
  • Flutter 开发环境安装
  • 中科时代建设官方网站设计品牌logo
  • 【C++】模板 - - - 泛型编程的魔法模具,一键生成各类代码
  • Vue3知识详解(一)(基础知识部分)
  • 网站网页链接网站变灰色 html
  • Docker核心技术:深入理解网络模式 ——Bridge模式全栈实战与性能调优
  • Spring Web MVC构建现代Java Web应用的基石
  • 如何做tiktok的数据排行网站手机网站页面大小