资讯动态

Promise 从入门到进阶:核心概念、状态机制与实战 API 解析

发布时间:2026/8/7 12:50:44 来源:尧图企业网站定制
什么是 Promise在谈 Promise 是什么之前我们首先需要知道一个概念【异步编程】那异步编程是什么呢简单来说异步编程是一种编程范式目的是避免阻塞主线程通过事件循环 任务队列的方式把任务的执行从顺序流变成了事件流从而去解决单线程中的同步阻塞的问题。在早期异步编程的解决方案是使用 callback但是随着 callback 的使用出现了 callback 层层嵌套回调地狱和错误处理困难等问题。为了解决 callback 带来的问题 ES6 提供了一个新的异步解决方案也就是 Promise。它通过链式调用把纵向嵌套的回调拉平为横向流水线来解决回调地狱的问题同时使用 then 和 catch 方法来优雅的进行错误处理。异步操作只是实现异步编程的手段实际开发中常用的有callback、Promise、async/awaitPromise 的语法糖、setTimeout、addEventListener 等。Promise 的状态Promise 实例中有两个内置属性[[PromiseState]] 存储状态[[PromiseResult]] : 存储结果我们首先来看下Promise的状态它有三种状态pending、fulfilledresolved、 rejected这三种状态具备以下特点状态的流转是单向的并且只能是pending fulfilled、pending rejected。状态一旦改变就会被锁死不受外界的影响。为了更好的理解Promise状态的特性需要说明一下Promise的设计思想是“单一终值”只关心异步操作的最终结果。而Promise的设计是基于Promise/A规范它是一种约束规范简单说下以下几点只定义了三种状态且三种状态单向冻结。没有cancel方法和abort状态new Promise中的代码是同步立即执行的。所以Promise创建就会立即执行无法中途取消。由于Promise的设计是”单一终值的“不传递中间过程所以pending是处于一种黑盒状态即当Promise处于pending状态的时候无法得知目前所处阶段是刚开始还是即将完成。错误不自动外抛因为错误会内化为 rejected 状态不转为同步异常所以需要使用.then或.catch来消费这个 rejected 状态。then必须返回新的promise 支持缺省参数、异常穿透、多次调用。支持thenable链式解析 任何带有 then 方法的对象或函数都是 thenable。Promise 的常用 API一、Promise 原型方法Promise的原型方法只有三个then、catch、finally。Promise.prototype.then()给 promise 注册成功 / 失败的回调并返回新的 promise值会穿透错误会向后传播。是微任务。忘记return下一层会拿到undefined。throw 抛错新的promise会rejected。循环引用会触发TypeError。// 循环引用 const p Promise.resolve(1); const p2 p.then(() p2); // 回调返回了 p2 自己形成循环触发 TypeError // 忘记return Promise.resolve(1) .then(v { console.log(v); }) // 没有 return .then(v console.log(v)); // undefinedPromise.prototype.catch()是 .then (null, onRejected) 的语法糖用来捕获链式调用中前面任何 Promise 抛出的异常或 reject 状态。是微任务。不会捕获它自己回调里的错误需要下一个 catch 来接。返回的是一个 promise 对象后面还可以继续链式调用。和 try...catch 的区别try...catch 是语法结构捕获当前调用栈里的同步异常。catch 是方法调用以微任务形式执行只能捕获当前 Promise 前面节点中的 Rejection。Promise.prototype.finally()是.then (onFinally, onFinally) 的语法糖不过 promise 的状态是成功还是失败都会执行。是微任务。无参数所以无法得知 promise 的状态。值会透传resolve 和 reject 的值都会透传。finally 里 抛出异常 或返回 rejected Promise才会变成 rejected。在实际开发中会在 finally 中关闭加载动画 loading添加日志埋点。const p Promise.resolve(ok); p .then(v console.log(成功:, v)) // 处理 fulfilled .catch(e console.log(失败:, e)) // 处理 rejected .finally(() { hideLoading(); // 无论成败都关掉 loading sendLog(request); // 埋点 });二、Promise 的常用静态方法Promise.all()是并行执行全部成功才成功一个失败就整体失败的方法。接受一个可迭代对象作为入参如果参数是非可迭代对象则会抛出 TypeError如果传入 空数组会立刻被 resolve 一个空数组。它的出参有两种全部成功按入参顺序返回一个数组。有一个rejected返回第一个被rejected的结果但其余请求不会被中断结果被忽略需要在外部手动取消。const controller new AbortController(); const fetch1 fetch(/api/1, { signal: controller.signal }); const fetch2 fetch(/api/2, { signal: controller.signal }); Promise.all([fetch1, fetch2]).catch(() { controller.abort(); // 手动取消其他请求 });Promise.allSettled()也是并行执行但和 all 方法不同的是它会等待所有 promise 都落定后再给结果返回值包含了结果和状态。它的特点是永远不会 reject不管成功失败都会 resolve。Promise.allSettled([ Promise.resolve(ok), Promise.reject(err) ]).then(results console.log(results)); // [{status: fulfilled, value: ok}, {status: rejected, reason: err}]Promise.race()是竞速是先看谁状态落定最先状态改变的决定整体结果不管是成功还是失败。只返回第一个状态落定的结果其余的请求会继续但结果忽略。入参如果是空数组则会永远处于 pending 状态。const p1 new Promise(res setTimeout(() res(慢), 100)); const p2 Promise.reject(快); // 立即 rejected Promise.race([p1, p2]) .catch(e console.log(e)); // 快Promise.any()取第一个成功的结果全部失败才 reject会抛 AggregateError里面包含所有失败的原因比 race 更容错。const promises [ Promise.reject(err1), new Promise(res setTimeout(() res(ok), 100)), Promise.reject(err2) ]; // race第一个 settled 是 reject直接失败 Promise.race(promises).catch(e console.log(race:, e)); // race: err1 // any忽略 reject等待第一个 fulfilled Promise.any(promises).then(v console.log(any:, v)); // any: ok100ms 后Promise.resolve()它针对不同参数有不同处理方式分别是传入 promise原样返回不做二次包装。传入 thenable将其转换为 promise 对象立即执行它的 then 方法。传入普通值立即返回 fulfilled 状态的普通值。开发中的使用场景微任务插队把同步快速变 异步、确保返回值一定是 Promise。// 1. Promise 实例原样返回 const p new Promise(() {}); Promise.resolve(p) p; // true // 2. Thenable递归解析 const thenable { then(resolve, reject) { reject(new Error(from thenable)); // 可以是 rejected } }; Promise.resolve(thenable).catch(e console.log(e.message)); // from thenable // 3. 普通值fulfilled Promise.resolve(42).then(v console.log(v)); // 42Promise.reject()是用来创建一个已被拒绝的 promise 的快捷方式把任何值立即变 rejected 状态的静态方法对参数不解析传入什么就以什么作为失败原因原样返回。开发中的使用场景提前抛错统一错误处理条件失败短路。function fetchUser(id) { if (!id) { return Promise.reject(new Error(id 不能为空)); } return fetch(/api/user/${id}); }Promise 的基础应用一、取消 Promise方案一使用原生的取消机制 AbortControllerconst controller new AbortController(); const signal controller.signal; fetch(/api/data, { signal }) .then(response response.json()) .then(data console.log(data)) .catch(err { if (err.name AbortError) { console.log(请求已取消); } }); // 取消请求 controller.abort();方案二利用 Promise.race 竞速模拟取消function withCancel(promise, cancelPromise) { return Promise.race([ promise, cancelPromise.then(() { throw new Error(Cancelled); }) ]); } const task new Promise(resolve setTimeout(() resolve(done), 5000)); const cancel new Promise(resolve setTimeout(resolve, 2000)); // 2秒后触发取消 withCancel(task, cancel).catch(err console.log(err.message)); // Cancelled除了示例的两种方式还可以使用一个外部标志位cancel token模式来控制Promise的执行流程以及一些三方库。二、改变 Promise 的状态在new Promise中改变Promise的状态的方法只有三种resolve、reject、throw。const p new Promise((resolve, reject) { resolve(数据加载成功); // 状态变为 fulfilled }); p.then(v console.log(v)); // 数据加载成功 const p1 new Promise((resolve, reject) { reject(new Error(数据加载失败)); // 状态变为 rejected }); p1.catch(e console.log(e.message)); // 数据加载失败 const p2 new Promise((resolve, reject) { throw new Error(参数非法); // 等效于 reject(new Error(参数非法)) }); p2.catch(e console.log(e.message)); // 参数非法需要注意一下reject和throw的区别reject只改变状态不会中断函数的执行也就是写在reject后面的代码依旧会继续执行。throw会立即中断当前函数的执行。三、中断 Promise// 返回一个pending状态的promise但不推荐 // 更优雅的处理还是使用AbortController、throw/reject Promise.resolve(start) .then(v { console.log(v); return new Promise(() {}); }) .then(v console.log(这行永远不会执行)) .catch(e console.log(这行也永远不会执行));Promise 系列持续更新中...《从零手写 Promise深入理解异步编程核心原理》《Promise 工程化实战请求并发、链式封装与错误治理》《EventLoop 深度解析从浏览器原理到项目实战》

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价