一、 Promise详解
a. Promise核心特性(最关键)
1.状态不可逆(三种状态,一旦定型无法改变) Promise 存在且仅存在三种状态,状态的转换只有两种单向路径,不可逆、不可重复。
- 初始状态:PromiseState=
pending(进行中)- 执行resolve(),成功状态:PromiseState=
fulfilled(已完成)- 执行reject(),失败状态:PromiseState=
rejected(已拒绝)- 转换路径 1:
pending→fulfilled(调用resolve()触发)- 转换路径 2:
pending→rejected(调用reject()或执行器内部抛出错误触发)- 示例:一旦状态变为
fulfilled,后续再调用reject()也无法改变状态,结果值也会保持不变。- Promise中有throw的话,就相当于执行了reject
2.结果值唯一(状态定型后,携带唯一的结果 / 原因)
- then接收两个回调,一个成功回调,一个失败回调
- 状态变为
fulfilled时,会携带一个唯一的「成功结果值」(value),存储在 Promise 内部,可通过then成功回调获取;- 状态变为
rejected时,会携带一个唯一的「失败原因」(reason),存储在 Promise 内部,可通过then失败回调或catch获取;- 这个值一旦确定,就不会被修改,后续访问都会得到相同的结果。
3.异步执行特性(回调函数异步触发,符合微任务规范) Promise 的回调(then/catch/finally)永远不会同步执行。
- 原生 Promise 回调属于微任务,会在当前同步代码执行完毕后、下一轮宏任务执行前触发(优先于
setTimeout等宏任务);- 这一特性避免了回调嵌套导致的「执行顺序混乱」,也符合 JavaScript 事件循环机制。
4.链式调用(支持无限链式 then ,实现异步流程串行化)
then()方法必然会返回一个新的 Promise 实例(不是原来的 Promise 实例),这是链式调用的基础;新 Promise 的状态和结果,由上一个
then回调的返回值决定:
若回调返回非 Promise 值(基本类型、普通对象等),新 Promise 状态为
fulfilled,结果为该返回值;若回调抛出错误,新 Promise 状态为
rejected,结果为该错误信息;若回调返回一个 Promise 实例,新 Promise 会沿用该实例的状态和结果;
- 链式调用解决了传统回调函数的「回调地狱」问题,让异步流程更清晰。
b. Promise 关键行为
1.回调队列管理(支持多个 then 绑定,依次触发) 一个 Promise 实例可以多次调用 then() 方法,绑定多个成功 / 失败回调。
- 若 Promise 状态未定型(
pending),回调会被存入对应的「回调队列」(成功回调队列 / 失败回调队列);- 若 Promise 状态已定型,回调会被异步推入微任务队列,等待执行;
- 状态定型后,后续绑定的回调也能正常触发,获取到已确定的结果值。
2.错误冒泡(链式调用中,错误会向下传递,直到被捕获)
- 链式调用中,任何一个环节抛出错误(或 Promise 变为
rejected),如果当前没有对应的onRejected回调,错误会向下冒泡,被后续的catch或then失败回调捕获;- 这一特性让错误处理更统一,无需在每个
then中都绑定失败回调。
3.值穿透( then 传入非函数参数时,会自动透传结果) 若 then() 方法传入的不是函数(如 null、undefined 等),会被自动忽略,等价于传递了一个「透传函数」,结果会直接传递给下一个 then。
- 示例:
promise.then(null).then(res => console.log(res))等价于promise.then(res => res).then(res => console.log(res));- 这一特性保证了链式调用的连贯性,即使中间某个
then未传入有效回调,也不会中断流程。
c. Promise 使用优势
- 解决「回调地狱」(Callback Hell):将嵌套的异步回调转为扁平的链式调用,代码可读性和可维护性大幅提升;
- 统一异步错误处理:通过
catch实现全局错误捕获,避免传统回调中「错误处理分散」的问题;- 支持多种异步流程控制:通过
Promise.all()、Promise.race()、Promise.allSettled()等静态方法,轻松实现「并行执行」「竞速执行」等复杂异步流程;- 是
async/await的基础:async/await是 Promise 的语法糖,只有理解 Promise,才能熟练掌握async/await的使用。
d. 总结
Promise 的核心可以概括为 3 个关键词:
- 状态不可逆:三种状态,单向转换,结果唯一;
- 异步执行:回调属于微任务,避免同步阻塞和顺序混乱;
- 链式调用:返回新 Promise,解决回调地狱,支持复杂异步流程。
这些特性共同让 Promise 成为 JavaScript 异步编程的核心方案,也是后续学习 async/await、前端工程化异步流程的基础。
二、手写Promise
class MyPromise{
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
static FUNCTION = 'function';
constructor(executor){
this.initValue();
this.initBind();
try{
executor(this.resolve,this.reject);
}catch(err){
this.reject(err)
}
}
initValue(){
this.PromiseState = MyPromise.PENDING;
this.PromiseResult = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind(){
//绑定this的原因
//resolve 和 reject 是 MyPromise 实例的方法(属于 this),
//但当我们把它们作为参数传给 executor 函数
//(也就是 new MyPromise((resolve, reject) => { ... }) 里的回调)时,
//函数的 this 指向会丢失。
//不绑定,执行时 this 是 undefined(严格模式下)
//this.resolve 是一个函数引用,当把它单独传给 executor 时,
//它就成了 “裸函数”,执行时失去了原本的 this 上下文;
//在严格模式下(ES6 类默认开启严格模式),裸函数执行的 this 是 undefined,
//而非 MyPromise 实例;
//所以 resolve 里的 this.PromiseState 会因为 this 是 undefined 而报错。
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
resolve(value){
if(this.PromiseState !== MyPromise.PENDING) return//状态不可逆
this.PromiseState = MyPromise.FULFILLED;//成功
this.PromiseResult = value;
while(this.onFulfilledCallbacks.length){
this.onFulfilledCallbacks.shift()(this.PromiseResult);
}
}
reject(reason){
if(this.PromiseState !== MyPromise.PENDING) return//状态不可逆
this.PromiseState = MyPromise.REJECTED;//失败
this.PromiseResult = reason;
while(this.onRejectedCallbacks.length){
this.onRejectedCallbacks.shift()(this.PromiseResult);
}
}
then(onFulfilled,onRejected){
//保证链式调用不中断
onFulfilled = typeof onFulfilled === MyPromise.FUNCTION ? onFulfilled :val => val;
//为什么要判断onFulfilled是否是函数
//链式调用中断:如果用户没传 onFulfilled(比如 promise.then().then(...)),
//onFulfilled 是 undefined,执行 undefined(this.PromiseResult) 会直接报错,
//链式调用直接崩掉;
//值无法透传:Promise 设计的核心是 “值的穿透”
//如果上一个 then 没有处理成功值,下一个 then 应该能拿到这个值。
//比如 promise.then().then(res => console.log(res)),
//第二个 then 必须能拿到第一个 Promise 的结果,
//而这个 “透传” 就是靠 val => val 这个默认回调实现的。
onRejected = typeof onRejected === MyPromise.FUNCTION ?onRejected :reason => {throw reason}
var thenPromise = new MyPromise((resolve,reject)=>{
const resovePromise = cb=>{
// 异步执行:符合 Promise 规范
queueMicrotask(()=>{
try{
const x = cb(this.PromiseResult);//成功onFulfilled(this.PromiseResult)
if(x === thenPromise){throw new TypeError('not self')}
//异步操作,如果上一个 then 返回的是一个新 Promise,
//下一个 then 必须等待这个新 Promise 完成,才能拿到它的结果。
if(x instanceof MyPromise){
//x 是一个 “未完成 / 已完成” 的 Promise,
//它的结果需要通过 then 才能获取
x.then(resolve,reject)
}else{
//x 是普通值(数字、字符串、对象、null 等),
//它的结果是 “立即确定” 的,不需要等待
resolve(x)
}
}catch(e){
this.reject(e)
}
})
}
if(this.PromiseState === MyPromise.FULFILLED){
resovePromise(onFulfilled);
}else if(this.PromiseState === MyPromise.REJECTED){
resovePromise(onRejected);
}else if(this.PromiseState === MyPromise.PENDING){
this.onFulfilledCallbacks.push(resovePromise.bind(this,onFulfilled));
this.onRejectedCallbacks.push(resovePromise.bind(this,onRejected));
}
})
return thenPromise
}
}
最后
这是《JavaScript系列》第9篇,将持续更新。
小伙伴如果喜欢我的分享,可以动动您发财的手关注下我,我会持续更新的!!!
您对我的关注、点赞和收藏,是对我最大的支持!欢迎关注、评论、讨论和指正!