2026/1/26 20:23:47
网站建设
项目流程
北京杰诚 做网站,做外汇模拟的网站,宝安公司网站建设比较好的,大连装修公司哪家口碑最好JavaScript学习笔记#xff1a;13.Promise
上一篇咱们用“设计图纸”搞定了类的封装与继承#xff0c;这一篇要攻克JS开发的“异步老大难”——Promise。做前端绕不开异步#xff1a;请求接口要等服务器响应、加载图片要等资源下载、定时器要等时间触发…… 而在Promise出现…JavaScript学习笔记13.Promise上一篇咱们用“设计图纸”搞定了类的封装与继承这一篇要攻克JS开发的“异步老大难”——Promise。做前端绕不开异步请求接口要等服务器响应、加载图片要等资源下载、定时器要等时间触发…… 而在Promise出现前咱们只能用“回调函数”处理这些操作写出来的代码常常像“俄罗斯套娃”——一层嵌一层这就是让人头秃的“回调地狱”。Promise的出现就像给异步任务发了一封“正式承诺函”它明确告诉程序“这个任务要么成功给你结果要么失败给你原因在此之前你该干啥干啥”。今天咱们就从“为什么需要Promise”讲起用“餐厅点餐”的生活化比喻把Promise的状态机制、链式调用、静态方法和实战技巧彻底讲透让你再也不用面对嵌套的回调皱眉头。一、先破案为什么需要Promise回调地狱有多坑先看一个场景先点奶茶异步奶茶做好后点蛋糕异步蛋糕做好后打包带走异步。用传统回调函数写是这样的// 回调地狱一层套一层代码向右偏移维护性极差orderMilkTea(珍珠奶茶,function(milkTea){console.log(拿到${milkTea});orderCake(芝士蛋糕,function(cake){console.log(拿到${cake});packFood([milkTea,cake],function(packed){console.log(打包完成${packed});// 再嵌套更多异步操作代码就彻底乱了},function(err){console.log(打包失败,err);});},function(err){console.log(点蛋糕失败,err);});},function(err){console.log(点奶茶失败,err);});这种代码的问题显而易见嵌套层级深像爬楼梯一样越往后越靠右可读性差错误处理分散每个回调都要单独写错误处理重复且冗余无法并行/中断多个异步任务只能串行想并行处理或中途中断非常麻烦。而用Promise改写后代码瞬间“站直了”// Promise链式调用平级代码清晰易懂orderMilkTea(珍珠奶茶).then(milkTea{console.log(拿到${milkTea});returnorderCake(芝士蛋糕);// 返回新的Promise衔接下一个任务}).then(cake{console.log(拿到${cake});returnpackFood([milkTea,cake]);}).then(packedconsole.log(打包完成${packed})).catch(errconsole.log(流程失败,err));// 统一错误处理 核心差异Promise把“嵌套的回调”变成了“链式的then”错误处理也统一交给最后一个catch代码结构瞬间清晰。这就是Promise的核心价值——规范化异步流程解决回调地狱。二、Promise核心概念一张“承诺函”的三个状态Promise翻译过来是“承诺”它的核心就是用“状态”来描述异步任务的执行结果。可以把Promise想象成“餐厅点餐单”pending等待中订单已提交厨师正在做任务还没完成fulfilled已成功餐做好了任务成功完成拿到结果rejected已失败食材用完了任务失败拿到错误原因。这三个状态有两个铁律是理解Promise的关键状态不可逆一旦从pending变成fulfilled或rejected就再也变不回去了——就像餐做好了不能再变回制作中没食材了也不能再上菜结果唯一成功时只有一个“结果值value”失败时只有一个“原因reason”不会出现既成功又失败的情况。三、Promise基础用法从“发承诺”到“收结果”Promise的用法就像“点餐→等餐→用餐”核心分三步创建Promise、改变状态、处理结果。1. 第一步创建Promise提交订单用new Promise()创建Promise实例接收一个“执行器函数”作为参数这个函数有两个内置参数resolve成功时调用相当于“上菜”和reject失败时调用相当于“告知没餐”。// 封装一个“点奶茶”的Promise函数functionorderMilkTea(type){// 执行器函数立即执行里面包含异步任务returnnewPromise((resolve,reject){setTimeout((){// 模拟异步操作1秒后完成consthasIngredientMath.random()0.3;// 70%概率有食材if(hasIngredient){// 成功调用resolve传递结果resolve(${type}少糖少冰);}else{// 失败调用reject传递错误原因reject(newError(点${type}失败珍珠卖完了));}},1000);});} 注意执行器函数会立即执行不是等到调用then才执行——就像提交订单后厨师立刻开始做不是等你催单才动手。2. 第二步处理结果用餐/处理没餐用then()处理成功结果用catch()处理失败结果还能加finally()处理“无论成功失败都要做的事”比如收起点餐单。// 调用Promise函数处理结果orderMilkTea(珍珠奶茶).then(result{// 成功回调接收resolve的结果console.log(用餐${result});}).catch(error{// 失败回调接收reject的原因console.log(处理失败${error.message});}).finally((){// 无论成功失败都会执行console.log(流程结束收起点餐单);});执行结果二选一// 成功情况 用餐珍珠奶茶少糖少冰 流程结束收起点餐单 // 失败情况 处理失败点珍珠奶茶失败珍珠卖完了 流程结束收起点餐单四、核心技巧链式调用——异步任务的“流水线”Promise的灵魂是“链式调用”then()会返回一个新的Promise让多个异步任务像流水线一样依次执行而不是嵌套。1. 链式调用的核心逻辑每个then()的返回值会成为下一个then()的参数如果then()返回的是Promise下一个then()会等待这个Promise完成再接收结果任何一个环节抛出错误都会跳过后续then()直接进入最近的catch()。// 流水线示例点奶茶→点蛋糕→打包orderMilkTea(珍珠奶茶).then(milkTea{console.log(拿到奶茶${milkTea});returnorderCake(芝士蛋糕);// 返回新Promise衔接下一个任务}).then(cake{console.log(拿到蛋糕${cake});returnpackFood([milkTea,cake]);// 继续返回Promise}).then(packedconsole.log(打包完成${packed})).catch(errconsole.log(流程失败${err.message}));2. 常见坑忘记return导致“漂浮Promise”如果then()里启动了异步任务但没返回它这个任务就会“漂浮”——无法追踪状态下一个then()会提前执行。// 反面例子忘记returnfetch是漂浮PromiseorderMilkTea(珍珠奶茶).then(milkTea{fetch(/api/recordOrder);// 没返回无法追踪结果}).then((){console.log(订单记录完成);// 可能在fetch完成前执行});// 正面例子返回Promise确保顺序orderMilkTea(珍珠奶茶).then(milkTea{returnfetch(/api/recordOrder);// 返回Promise下一个then等待它完成}).then((){console.log(订单记录完成);// 正确fetch完成后执行});五、错误处理Promise的“安全网”Promise的错误处理就像“餐厅的投诉渠道”能统一捕获整个链条的错误还支持细粒度处理。1. 统一错误处理全局投诉用链条末尾的catch()捕获所有环节的错误包括then()里抛出的异常orderMilkTea(珍珠奶茶).then(milkTea{if(milkTea.includes(珍珠)){thrownewError(我不爱喝珍珠换椰果);// 手动抛出错误}returnorderCake(芝士蛋糕);}).then(cakepackFood([milkTea,cake])).catch(errconsole.log(统一处理错误${err.message}));// 输出统一处理错误我不爱喝珍珠换椰果2. 细粒度错误处理局部投诉嵌套then()里的catch()只处理当前环节的错误不影响后续流程orderMilkTea(珍珠奶茶).then(milkTea{// 可选环节加配料失败不影响主流程returnaddTopping(milkTea,椰果).catch(err{console.log(加配料失败${err.message});returnmilkTea;// 返回原奶茶流程继续});}).then(milkTeaorderCake(芝士蛋糕)).then(cakepackFood([milkTea,cake])).catch(errconsole.log(主流程失败${err.message}));3. catch后的链式恢复catch()后还能继续接then()实现“错误恢复”——就像投诉后餐厅补救流程继续orderMilkTea(珍珠奶茶).then(milkTea{thrownewError(奶茶太甜了);}).catch(err{console.log(处理错误${err.message}换无糖的);returnorderMilkTea(珍珠奶茶无糖);// 补救返回新Promise}).then(milkTeaconsole.log(最终拿到${milkTea})).finally(()console.log(流程结束));六、静态方法Promise的“团队协作模式”Promise提供了四个静态方法处理多个异步任务的协作就像“餐厅处理多个订单”的不同策略。方法作用核心特点场景示例Promise.all([p1,p2,p3])所有任务成功才成功一个失败就失败快速失败结果按顺序返回同时点奶茶和蛋糕都做好才打包Promise.allSettled([p1,p2,p3])等待所有任务完成无论成功失败全部 settle返回每个任务的状态和结果统计多个接口的请求结果成功/失败都要知道Promise.any([p1,p2,p3])任意一个任务成功就成功全部失败才失败快速成功返回第一个成功的结果多个CDN加载图片哪个快用哪个Promise.race([p1,p2,p3])任意一个任务 settle成功/失败就结束先到先得返回第一个 settle 的结果接口请求超时控制请求和定时器赛跑实战示例// 1. Promise.all全部成功才返回constp1orderMilkTea(珍珠奶茶);constp2orderCake(芝士蛋糕);Promise.all([p1,p2]).then([milkTea,cake]packFood([milkTea,cake])).then(packedconsole.log(打包完成${packed})).catch(errconsole.log(有订单失败${err.message}));// 2. Promise.race超时控制constrequestfetch(/api/data);consttimeoutnewPromise((_,reject){setTimeout(()reject(newError(请求超时)),5000);});Promise.race([request,timeout]).then(dataconsole.log(请求成功,data)).catch(errconsole.log(请求失败,err.message));七、async/awaitPromise的“语法糖”写异步像写同步async/await是ES2017新增的语法基于Promise让异步代码看起来像同步代码更易读。1. 基础用法async修饰函数函数返回值自动变成Promiseawait只能在async函数内使用等待Promise完成并返回结果错误用try/catch捕获和同步代码的错误处理一致。// 用async/await改写之前的流水线asyncfunctionorderMeal(){try{// 等待奶茶做好constmilkTeaawaitorderMilkTea(珍珠奶茶);console.log(拿到奶茶${milkTea});// 等待蛋糕做好constcakeawaitorderCake(芝士蛋糕);console.log(拿到蛋糕${cake});// 等待打包constpackedawaitpackFood([milkTea,cake]);console.log(打包完成${packed});returnpacked;}catch(err){console.log(流程失败${err.message});}finally{console.log(流程结束收起点餐单);}}// 调用async函数返回PromiseorderMeal();2. 并发处理await Promise.allawait配合Promise.all能实现并发异步任务asyncfunctionorderBatch(){try{// 同时发起两个请求并发执行const[milkTea,cake]awaitPromise.all([orderMilkTea(珍珠奶茶),orderCake(芝士蛋糕)]);console.log(同时拿到${milkTea}、${cake});}catch(err){console.log(失败${err.message});}}八、避坑指南Promise的“常见陷阱”状态不可逆一旦resolve/reject后续再调用resolve/reject无效忘记return Promise导致链条断裂下一个then提前执行错误吞噬嵌套then里的错误若没catch会被外层catch捕获但可能掩盖真正的错误位置同步错误执行器函数里的同步错误会直接导致Promise reject需用try/catch捕获微任务时序then回调是微任务会在当前同步代码执行完后、下一轮事件循环前执行和setTimeout的任务队列不同。// 微任务时序示例console.log(1. 同步代码开始);Promise.resolve().then(()console.log(2. Promise then微任务));setTimeout(()console.log(3. setTimeout任务队列),0);console.log(4. 同步代码结束);// 输出顺序1 → 4 → 2 → 3九、总结Promise的核心价值Promise的本质是“异步任务的规范化管理工具”它解决了回调地狱的痛点提供了清晰的链式流程和统一的错误处理而async/await让它的用法更接近同步代码。核心价值总结把“嵌套回调”变成“扁平链式”可读性提升统一错误处理避免重复代码支持多个异步任务的协作并发、竞速等为async/await打下基础简化异步编程。Promise是JS异步编程的基石掌握它的状态机制、链式调用和静态方法能让你从容应对接口请求、资源加载等各种异步场景。