【面试题】Promise在异步操作中到底扮演什么角色?若将其理解为状态管理器,有哪些更丰富的应用场景?

前言

很多同学都不理解Promise,Promise一度都是前端同学的摆设,基本都是封装一下Ajax请求就完了,其他的功能也就没用过了,这个面试题是一个启发式面试题,启发前端同学对知识有一种思考,真正理解知识,在理解的基础上,用起来。

如果你仅仅知道这玩意解决了回调地狱的问题,那就太小瞧了Promise重要性,它比你想象的更重要,更强大,更不可或缺。

举一个让你纠结的场景

大家都知道stable diffusion里面有很多按钮和参数,如果每个按钮都可能触发异步请求,但要保证异步返回的顺序,因为请求的顺序一旦打乱,生成的图片的就不同,当然stable diffusion并没有这么干,我只是是举个形象的例子,以反映Promise如何简单地解决这个问题。

如果没有Promise,你需要搞一个队列,还要给每个请求设定一个request_id,还要后端配合你要把request_id返回来等等,还要写个轮询来监听所有的返回结果,让他们按顺序排好,但是有了Promise就变得特别简单,如下:

Promise.resole().then(() => new Promise(()=>{ /** 异步请求 **/}))

因为我们把Promise作为一个状态监控器,而不是回调地狱解决者。什么意思呢?意思就是Promise可以互相包裹,状态监控器再监视状态监控器,这个过程是循环往复的,这也解释了为何Promise可以无限嵌套的原因。

再举一个例子

谷歌插件开发中,popup.html需要与网页进行通信,并且还需要阻塞式响应,如果没有Promise,那你就得用生成器来写了,而如果你有Promise,应该是这样的

function sendMessageWithPromise(action) {return new Promise((resolve, reject) => {const messageId = Math.random().toString(36).substr(2); // 生成唯一ID// 发送消息chrome.runtime.sendMessage({ from: 'popup', action, messageId });// 添加监听器chrome.runtime.onMessage.addListener(function listener(response) {if (response.messageId === messageId) {// 监听到匹配的响应后移除监听器chrome.runtime.onMessage.removeListener(listener);if (response.error) {reject(response.error); // 处理错误} else {resolve(response.data); // 处理成功结果}}});});
}// 使用示例
const result = await sendMessageWithPromise('getData');

所以,要将Promise 从异步操作中拆分来看,它是异步状态的监控器,而不是执行异步过程,就像你是个律师,一定会给原告一个结果,但结果是法官来判决的,而你负责传达,这样原告就不用天天来问法官了。这就是Promise。

如果我们将 Promise 视为异步操作的状态管理器,它不仅仅是处理异步性的工具,还可以用来管理复杂任务的生命周期。这种视角揭示了 Promise 在以下场景中的强大应用:

1. 工作流管理

  • 场景:在需要按顺序执行多个异步任务时(例如,先获取用户信息,再获取用户的帖子,最后获取评论),Promise 可以管理每个步骤的状态。

  • 示例:

    javascript

     

    fetchUser().then(user => fetchPosts(user.id)).then(posts => fetchComments(posts[0].id)).then(comments => console.log(comments)).catch(error => console.error("工作流出错:", error));
  • 优势:每个 .then() 表示一个状态转换,清晰地展示了工作流的进展。

2. 并行任务执行

  • 场景:需要同时加载多个资源(例如图片、脚本或 API 数据),并在所有任务完成后继续处理。

  • 示例:

    javascript

     

    Promise.all([fetchImage(), fetchScript(), fetchData()]).then(results => console.log("所有资源加载完成:", results)).catch(error => console.error("某个资源加载失败:", error));
  • 优势:Promise.all() 管理多个 Promise 的集体状态,只有当所有任务都完成时才进入下一步。

3. 超时与延迟

  • 场景:为可能耗时过长的操作设置超时,或故意延迟某些操作。

  • 示例:

    javascript

     

    const timeoutPromise = new Promise((_, reject) =>setTimeout(() => reject(new Error("操作超时")), 5000)
    );
    Promise.race([fetchData(), timeoutPromise]).then(data => console.log("数据:", data)).catch(error => console.error("错误或超时:", error));
  • 优势:Promise.race() 管理竞争状态,优先处理最先完成(或失败)的 Promise。

4. 缓存与记忆化

  • 场景:避免重复执行相同的异步操作,通过缓存 Promise 的结果。

  • 示例:

    javascript

     

    let cachedPromise = null;
    function getData() {if (!cachedPromise) {cachedPromise = fetchData().then(data => data);}return cachedPromise;
    }
  • 优势:Promise 管理操作的状态,确保只执行一次请求,后续调用直接使用缓存结果。

5. 事件处理

  • 场景:将基于事件的异步操作转换为 Promise,便于与其他异步代码整合。

  • 示例:

    javascript

     

    function waitForClick(element) {return new Promise(resolve => {element.addEventListener("click", resolve, { once: true });});
    }
    waitForClick(button).then(() => console.log("按钮被点击!"));
  • 优势:Promise 管理事件的状态,在事件触发时决议,使代码更简洁。

6. 测试异步代码

  • 场景:为异步函数编写单元测试。

  • 示例:

    javascript

     

    test("fetchData 返回正确数据", async () => {const data = await fetchData();expect(data).toEqual(expectedData);
    });
  • 优势:结合 async/await,Promise 让异步测试更直观和可读。

7. 资源清理

  • 场景:在异步操作完成后(无论成功或失败),确保清理资源。

  • 示例:

    javascript

     

    openResource().then(resource => {// 使用资源}).catch(error => console.error("错误:", error)).finally(() => closeResource());
  • 优势:.finally() 管理状态的最终转换,确保清理逻辑始终执行。


三、为何这种理解有价值

将 Promise 视为状态管理器提供了一种更清晰的思维模型:

  • 生命周期清晰:异步任务的每个阶段(开始、进行、结束)通过状态体现出来,便于追踪。

  • 代码可预测:Promise 只能决议一次(fulfilled 或 rejected),避免了重复执行或竞争条件。

  • 组合性强:通过 Promise.all()、Promise.race() 等方法,可以轻松组合多个异步任务的状态。

  • 错误管理直观:将错误视为状态的一部分,使得错误处理更加集中和自然。

 

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

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

相关文章

浏览器渲染过程

浏览器的渲染过程是多个线程、进程和阶段的复杂编排,它将原始的 HTML、CSS 和 JavaScript 转换为屏幕上的交互像素。 你在浏览器中输入一个 URL 并按下回车键 网站在你的屏幕上呈现出来 注意:本文中,将使用 “客户端(client&am…

华鲲振宇天工TG225 B1国产服务器试装openEuler22.03 -SP4系统

今天测试了一下在华鲲振宇公司的天工TG225 B1国产服务器上进行openEuler22.03 -SP4操作系统的试装,本文记录整个测试过程。 一、服务器信息 1、服务器型号 Huakun TG225 B1 (D) 2、登录IPMI帐户信息 初始用户名Tech.ON 密码TianGong8000 二、磁盘RAID配置 测试…

Qemu-STM32(十二):STM32F103 框架代码添加

简介 本系列博客主要描述了STMF103的qemu模拟器实现,进行该项目的原因有两点: 作者在高铁上,想在STM32F103上验证一个软件框架时,如果此时掏出开发板,然后接一堆的线,旁边的人估计会投来异样的目光,特别是…

英伟达与通用汽车深化合作,澳特证券am broker助力科技投资

在近期的GTC大会上,英伟达CEO黄仁勋宣布英伟达将与通用汽车深化合作,共同推进AI技术在自动驾驶和智能工厂的应用。此次合作标志着自动驾驶汽车时代的加速到来,同时也展示了英伟达在AI技术领域的最新进展。      合作内容包括:…

将 Markdown 表格结构转换为Excel 文件

在数据管理和文档编写过程中,我们经常使用 Markdown 来记录表格数据。然而,Markdown 格式的表格在实际应用中不如 Excel 方便,特别是需要进一步处理数据时。因此,我们开发了一个使用 wxPython 的 GUI 工具,将 Markdown…

HarmonyOS NEXT 关于鸿蒙的一多开发(一次开发,多端部署) 1+8+N

官方定义 定义:一套代码工程,一次开发上架,多端按需部署。 目标:支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。 什么是18…

Nacos

简介 Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台,旨在为微服务架构提供高可用、高性能的解决方案。其核心功能包括服务注册与发现、动态配置管理、服务健康监测、动态 DNS …

Win11系统下qq远程不能控制对方电脑(鼠标点不动)的解决方法

在被控制的电脑上,打开控制面板,点击系统和安全 点击更改用户账户控制设置 下拉用户控制设置至最低,从不通知,点击确定 返回控制面板系统与安全,带年纪允许远程访问 点击允许远程协助连接这台计算机 重启电脑 再次打…

猎豹移动营收连续三季增长,AI驱动的猎豹成绩单怎么分析?

3月26日,猎豹移动发布2024年Q4及全年财报,这份财报我们到底该该怎么分析呢? 首先,整体财务表现稳健,营收连续三季增长。从财务数据来看,猎豹移动整体表现稳健。2024年Q4及全年财报显示,总收入达…

函数:链式访问

链式访问是将函数的返回值当作回传值就是链式访问 这是原本的字符数回传代码 int main() {int len strlen("seig heil");printf("%d", len);return 0; } 运行结果: 这是链式访问的代码: int main() {printf("%d\n",s…

C++ map容器总结

map基本概念 简介: map中所有元素都是pair pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值) 所有元素都会根据元素的键值自动排序 本质: map/multimap属于关…

23种设计模式-代理(Proxy)设计模式

代理设计模式 🚩什么是代理设计模式?🚩代理设计模式的特点🚩代理设计模式的结构🚩代理设计模式的优缺点🚩代理设计模式的Java实现🚩代码总结🚩总结 🚩什么是代理设计模式…

UE4学习笔记 FPS游戏制作29 更换武器时更换武器的图标

文章目录 制作物体图标UI添加获取武器图标的方法使用事件分发器,通知UI要换枪定义事件分发器调用事件分发器注册事件分发器 制作物体图标UI 在Fpp-UI上添加一个图片,改名为五weaponIcon,勾选SizeToContent,锚点放在右下角,对齐改…

Chrome 开发环境快速屏蔽 CORS 跨域限制!

Chrome 开发环境快速屏蔽 CORS 跨域限制【详细教程】 ❓ 为什么需要临时屏蔽 CORS? 在前后端开发过程中,我们经常会遇到 跨域请求被浏览器拦截 的问题。例如,你在 http://localhost:3000 调用 https://api.example.com 时,可能会…

【RAG综述系列】之 RAG 相关背景和基本原理

系列文章: 【RAG综述系列】之 RAG 相关背景和基本原理 【RAG综述系列】之 RAG 特点与挑战以及方法与评估 【RAG综述系列】之 RAG 先进方法与综合评估 【RAG综述系列】之 RAG 应用和未来方向 正文: 检索增强生成(Retrieval-Augmented Gen…

德昂观点:如何看待MicroStrategy改名为Strategy?

2025年2月,纳斯达克上市公司MicroStrategy(股票代码:MSTR)宣布更名为“Strategy”,并同步启用全新品牌标识与橙色主视觉。这不仅是品牌形象的更新,更是公司战略方向的明确宣示。德昂作为MSTR中国区BI合作伙…

计算机视觉算法实战——手术导航:技术、应用与未来

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​ ​​​​​​​​​ ​​ 1. 手术导航中的计算机视觉:领域介绍 计算机视觉在手术导航领域的应用代表了现代医学与人工智…

Java全栈面试宝典:内存模型与Spring设计模式深度解析

目录 一、JVM内存模型进阶篇 🔥 问题13:堆与栈的六大维度对比 内存结构对比图 核心差异对照表 🔥 问题14:三区联动内存模型解析 代码内存分配图解 三区协作流程图 二、Spring设计模式全景解析 🌟 Spring框架七…

FALL靶场通关攻略

1,下载好靶机后打开,通过kali扫描靶机ip和端口,得到靶机ip为192.168.50.144 2,扫描目录 3,访问靶机 4,访问扫描到的test.php,得到缺少GET请求参数的提示 5,使用FUZZ来扫出参数为file 6&#xff…

《C++11:bind绑定器与function包装器》

CSTL中提供了bind1绑定器,通常与函数对象一起使用。 函数对象是重载了operator()函数的对象。 将二元函数对象operator()的第一个参数绑定为固定的x来构造一元函数对象。返回绑定了第一个参数的函数对象。 将二元函数…