JavaScript 的异步管家:彻底搞懂 Promise 原型方法

JavaScript 的异步管家:彻底搞懂 Promise 原型方法

🤔 为什么需要 Promise?

在 ES6 之前,处理异步操作(如网络请求、定时器)主要靠回调函数(Callback)。当逻辑复杂时,容易陷入“回调地狱”(Callback Hell),代码嵌套层级深,难以维护。

通俗比喻
想象你去餐厅点餐(发起异步请求)。

  • 回调地狱模式:你站在柜台前,服务员说“等菜好了叫你”,你不敢走;菜好了,服务员说“等汤好了叫你”,你还得站着……直到所有流程结束。
  • Promise 模式:服务员给你一张取餐号小票(Promise 对象)。你可以去旁边坐着玩手机(执行其他代码)。
    • 如果菜做好了(成功),服务员叫号,你去取餐(.then)。
    • 如果菜卖完了(失败),服务员道歉,你决定换一家或吃泡面(.catch)。
    • 无论成功还是失败,最后都要把桌子收拾干净(.finally)。

这张“小票”及其附带的服务规则,就是Promise 原型上的方法


📂 目录

  1. 🛠️ 核心概念:Promise 的状态机
  2. 🔧 实例方法:处理结果与异常
  3. 🏭 静态方法:并发控制与工具函数
  4. 💻 代码实战:常见场景演练
  5. ⚠️ 常见误区与最佳实践
  6. 💡 总结

1. 🛠️ 核心概念:Promise 的状态机

Promise 对象代表一个异步操作的最终完成(或失败)及其结果值。它有三种状态,且状态一旦改变,不可逆

状态英文说明
Pending进行中初始状态,既不是成功,也不是失败。
Fulfilled已成功操作成功完成,有一个值。
Rejected已失败操作失败,有一个原因(错误信息)。

注意:我们通常调用的.then(),.catch()等方法,都是挂载在Promise.prototype上的实例方法。而Promise.all(),Promise.race()等是挂载在Promise构造函数上的静态方法。


2. 🔧 实例方法:处理结果与异常

这些方法用于注册回调,当 Promise 状态改变时被调用。

✅ 1..then(onFulfilled, onRejected)

  • 作用:指定Resolved和Rejected状态的回调函数。
  • 返回值返回一个新的 Promise 对象。这使得我们可以进行链式调用
  • 参数
    • onFulfilled: 可选,成功时的回调。
    • onRejected: 可选,失败时的回调(通常建议用.catch代替)。
constpromise=newPromise((resolve,reject)=>{setTimeout(()=>resolve("数据加载成功"),1000);});promise.then((value)=>{console.log(value);// "数据加载成功"returnvalue+" - 处理完毕";// 返回值会传递给下一个 then}).then((newValue)=>{console.log(newValue);// "数据加载成功 - 处理完毕"});

✅ 2..catch(onRejected)

  • 作用:专门处理 Rejected 状态或链式中抛出的错误。
  • 本质:它是.then(null, onRejected)的语法糖。
  • 优势:可以捕获前面所有.then中发生的同步错误。
promise.then((value)=>{thrownewError("处理过程中出错了");}).catch((error)=>{console.error(error.message);// "处理过程中出错了"});

✅ 3..finally(onFinally)

  • 作用:无论 Promise 最终是 Fulfilled 还是 Rejected,都会执行的回调。
  • 场景:关闭 Loading 动画、隐藏弹窗、清理资源等。
  • 注意onFinally不接受参数,也不影响最终的返回值(除非抛出错误)。
letisLoading=true;fetch("/api/data").then((res)=>res.json()).catch((err)=>console.error(err)).finally(()=>{isLoading=false;// 无论成功失败,都停止加载状态console.log("请求结束");});

3. 🏭 静态方法:并发控制与工具函数

这些方法直接挂在Promise构造函数上,用于创建或组合多个 Promise。

✅ 1.Promise.resolve(value)

  • 作用:快速创建一个状态为 Fulfilled 的 Promise。
  • 场景:将普通值或非 Promise 对象包装成 Promise,以便统一使用.then处理。
Promise.resolve("Hello").then((val)=>console.log(val));// "Hello"

✅ 2.Promise.reject(reason)

  • 作用:快速创建一个状态为 Rejected 的 Promise。
  • 场景:在函数开头进行参数校验,失败时直接返回拒绝态。
functioncheckAge(age){if(age<18){returnPromise.reject("未成年人禁止访问");}returnPromise.resolve("访问允许");}

✅ 3.Promise.all(iterable)【高频面试点】

  • 作用:并行执行多个 Promise,所有都成功才成功,任何一个失败则立即失败。
  • 返回值:一个包含所有结果的数组(顺序与输入一致)。
  • 场景:同时请求用户信息和订单列表,两者都拿到后才渲染页面。
constp1=Promise.resolve(1);constp2=Promise.resolve(2);constp3=Promise.reject("Error");// 全部成功Promise.all([p1,p2]).then((values)=>console.log(values));// [1, 2]// 有一个失败Promise.all([p1,p3]).catch((err)=>console.error(err));// "Error"

✅ 4.Promise.race(iterable)

  • 作用:并行执行多个 Promise,谁先改变状态(无论成功还是失败),就采用谁的结果。
  • 场景:超时控制。比如请求接口,如果 5秒 没返回,就判定超时。
constrequest=fetch("/api/slow-data");consttimeout=newPromise((_,reject)=>setTimeout(()=>reject("请求超时"),5000),);Promise.race([request,timeout]).then((res)=>console.log("成功",res)).catch((err)=>console.error("失败",err));

✅ 5.Promise.allSettled(iterable)【ES2020】

  • 作用:并行执行多个 Promise,等待所有任务结束(不管成功还是失败)。
  • 返回值:一个对象数组,每个对象包含status(‘fulfilled’ 或 ‘rejected’) 和value/reason
  • 场景:批量上传文件,想知道哪些成功了,哪些失败了,而不是因为一个失败就全盘否定。
constp1=Promise.resolve(1);constp2=Promise.reject("Fail");Promise.allSettled([p1,p2]).then((results)=>{results.forEach((result)=>{if(result.status==="fulfilled"){console.log("成功:",result.value);}else{console.log("失败:",result.reason);}});});// 输出:// 成功: 1// 失败: Fail

✅ 6.Promise.any(iterable)【ES2021】

  • 作用:并行执行多个 Promise,只要有一个成功,就返回那个成功的结果。只有全部失败,才返回失败(聚合错误)。
  • 场景:从多个镜像源下载资源,哪个快用哪个。

4. 💻 代码实战:常见场景演练

场景 1:串行依赖请求(Chain)

第二个请求依赖第一个请求的结果。

getUserInfo(userId).then((user)=>{returngetOrderList(user.id);// 返回新的 Promise}).then((orders)=>{console.log("用户订单:",orders);}).catch((err)=>{console.error("流程出错:",err);});

场景 2:并行独立请求(All)

两个请求互不依赖,同时发起以节省时间。

Promise.all([getBannerData(),getRecommendList()]).then(([banners,recommends])=>{renderPage(banners,recommends);}).catch((err)=>{showToast("页面加载失败");});

场景 3:带超时的请求封装

functionfetchWithTimeout(url,timeout=5000){constcontroller=newAbortController();constid=setTimeout(()=>controller.abort(),timeout);returnfetch(url,{signal:controller.signal}).then((res)=>{clearTimeout(id);returnres.json();}).catch((err)=>{if(err.name==="AbortError"){thrownewError("请求超时");}throwerr;});}

5. ⚠️ 常见误区与最佳实践

❌ 误区 1:在.then中忘记return

如果在.then中返回了一个普通的值,下一个.then能收到;但如果返回了一个 Promise,下一个.then会等待这个 Promise 结算。如果不 return,后续链条可能拿到undefined

❌ 误区 2:混淆Promise.allPromise.allSettled

  • 如果你希望“要么全成,要么全败”,用all
  • 如果你希望“不管成败,我都要知道每个任务的结果”,用allSettled

✅ 最佳实践:始终使用.catchtry...catch

未处理的 Promise rejection 会导致控制台警告,甚至在 Node.js 进程中导致退出。

// Async/Await 风格下的错误处理asyncfunctionloadData(){try{constdata=awaitfetch("/api/data");// ...}catch(error){console.error("捕获异常:",error);}}

✅ 最佳实践:避免嵌套.then

尽量保持扁平化的链式调用,或者直接使用async/await,后者可读性更好。


6. 💡 总结

方法类型方法名核心作用关键特点
实例方法.then()处理成功/失败链式调用,返回新 Promise
.catch()处理异常捕获前面所有的错误
.finally()最终清理必执行,无参数
静态方法Promise.resolve()创建成功态包装值
Promise.reject()创建失败态包装错误
Promise.all()并行全成功短路与(一错即错)
Promise.race()竞速谁快听谁的
Promise.allSettled()并行全结算记录每个任务状态
Promise.any()竞速成功只要一个成功即可

🚀 博主寄语
Promise 是现代 JavaScript 异步编程的基石。
虽然async/await让代码看起来像同步的,但理解底层的 Promise 原型方法(特别是allrace的区别)对于处理复杂并发场景至关重要。

记住口诀
Then 链式传值忙,
Catch 兜底防异常。
All 要全都成功样,
Race 抢跑第一强。
Settled 不管成与败,
Any 只要一个亮。
Finally 最后收个场,
异步编程心不慌。

希望这篇文档能帮你彻底掌握 Promise 的原型方法!如果有疑问,欢迎在评论区留言。👇

喜欢这篇文章吗?记得点赞、收藏、转发哦!❤️