用一份外卖,看懂状态机+两个回调篓子
不少初学者看到完整版Promise手写源码就犯难,繁杂的边界处理和进阶优化让人望而生畏。其实抛开这些锦上添花的拓展逻辑,Promise的核心本质特别简单:一个状态机 + 两个回调篓子,所有功能都由此衍生。(手写Promise从来不是盲目造轮子,核心目的就是拆开原生API的黑盒,告别只会调用不懂原理的陌生感,彻底吃透异步运行的底层逻辑。)
大家或许对源码逻辑感到晦涩,但一定熟悉点外卖的日常。接下来我就用点外卖的生活化比喻,带你轻松弄懂状态机和回调篓子的核心运作逻辑。
先看逻辑思路
你点了一份外卖 = 发起一个异步任务
1. 状态机 = 外卖订单状态
- pending :商家正在做饭(任务进行中)
- fulfilled :外卖送到你手上(成功)
- rejected :商家没货/取消订单(失败)
状态机干了啥?
- 告诉你现在能不能吃
- 保证只会送一次,不会反复送
- 饭做好了就一直是做好的状态,不会变回“正在做”
- 结果会永久保存,你什么时候拿都有
(状态机 = 给异步任务定规矩: 只能走一次,走到哪就是哪,结果永久留着。)
2.回调篓子 = 你给外卖员留的“送达通知方式”
你还没拿到外卖时( pending ),你跟外卖员说:
- 送到了给我打电话
- 送不成给我发短信
这些“打电话、发短信”,就是你存在 then 里的回调。
回调篓子干了啥?
- 饭还没好,先把你的要求存起来
- 不催、不闹、不嵌套
- 等饭一好,一次性按顺序执行
(回调篓子 = 暂存你的“后续操作”, 异步没跑完,先排队等通知。)
3. 两者合在一起,才是 Promise
流程是这样的:
1. 你下单 → Promise 新建
2. 状态立刻变成 pending → 商家正在做饭
3. 你调用 .then() 留下回调 → 把“打电话/发短信”放进篓子存好
4.饭做好了 → resolve()
- 状态变成 fulfilled
- 拿出成功篓子里的所有回调,挨个执行 → 挨个打电话通知你
5.饭做不成 → reject()
- 状态变成 rejected
- 拿出失败篓子里的回调,挨个执行 → 发短信告诉你取消了
再到后面链式调用“骑手”干了什么,“平台”有哪些补救措施
(今天我们就以点外卖的方式: 从0 开始,一小块一小块叠代码,先懂原理再落地实现,彻底撕开 Promise 的黑盒!)
第一阶段:逐块拆解手写「纯状态机基础版Promise」
比喻以注释的形式写进代码里方便理解
下单必有初始状态,Promise 创建瞬间也必须固定 pending 态,状态只能单向流转,这是一切的根基。我们从零一块块码
第1块:定义三大核心状态常量(杜绝硬编码写错)
// 对标外卖三种固定状态:做饭中 / 已送达 / 已取消
const STATUS_PENDING = "pending"; // 等待中-正在做饭
const STATUS_FULFILLED = "fulfilled";// 成功-外卖送到
const STATUS_REJECTED = "rejected"; // 失败-订单取消
为什么单独定义常量? 统一管理状态名,全程复用,避免手写字符串拼写错误。(可以理解为有报错提示,同时也能让大家看代码更清晰的行业规范)
第2块:搭建Promise空类骨架(相当于开通下单通道)
// 创建自定义Promise类,开始搭建订单系统外壳
class _Promise {
// 构造函数:实例化瞬间触发 = 用户点击下单
constructor(executor = () => {}) {
}
}
executor 就是商家做饭的核心流程,默认给空函数防止报错。
第3块:构造器初始化核心属性(订单基础信息登记)
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
// 刚下单默认状态:商家正在做饭 pending
this.status = STATUS_PENDING;
// 预留位置:存放送到手的外卖成果(成功结果)
this.value = undefined;
// 预留位置:存放订单失败的原因(商家没货/超时)
this.reason = undefined;
}
}
核心规则:只要Promise一创建,天生固定 pending,绝不允许开局直接成功/失败。
第4块:内部定义resolve函数(外卖送达开关)
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// 专属开关:调用就代表外卖顺利送达
const resolve = (value) => {
// 铁律校验:只有还在做饭中,才能改成已送达
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
// 把到手的外卖存起来
this.value = value;
}
};
}
}
状态不可逆核心体现:已经送达/取消的订单,再也不能二次修改状态。
第5块:内部追加reject函数(订单取消开关)
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
}
};
// 专属开关:调用就代表订单作废取消
const reject = (reason) => {
// 同样铁律:只有做饭中才能取消订单
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
// 把取消原因记录下来
this.reason = reason;
}
};
}
}
第6块:立即执行executor做饭流程(下单立刻开工)
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
}
};
// 下单瞬间直接开火做饭!把两个开关交给外部掌控
executor(resolve, reject);
}
}
关键特性:Promise构造器里的执行器是同步立即执行,不会等待延迟。
第7块:追加基础then方法(外卖到了要干啥)
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
}
};
executor(resolve, reject);
}
// 定制收货操作:成功干啥、失败干啥
then(onFulfilled, onRejected) {
// 外卖已经送到,有成功回调就直接执行
if (this.status === STATUS_FULFILLED && typeof onFulfilled === 'function') {
onFulfilled(this.value);//这个onFulfilled传进来必须是函数,加判断为了防止报错崩程序
}
// 订单已经取消,有失败回调就直接执行
if (this.status === STATUS_REJECTED && typeof onRejected === 'function') {
onRejected(this.reason);//这里同理
}
// 注意:当前版本暂时处理不了还在做饭中的异步等待场景
}
}
第一阶段整合完整版(逐块拼装最终成品)
// 1. 定义外卖三大状态常量
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
// 2. 自定义基础Promise类
class _Promise {
constructor(executor = () => {}) {
// 3. 初始化订单默认状态和存储容器
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// 4. 送达开关逻辑
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
}
};
// 5. 取消开关逻辑
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
}
};
// 6. 立刻启动做饭流程
executor(resolve, reject);
}
// 7. 收货处理then方法
then(onFulfilled, onRejected) {
if (this.status === STATUS_FULFILLED && typeof onFulfilled === 'function') {
onFulfilled(this.value);
}
if (this.status === STATUS_REJECTED && typeof onRejected === 'function') {
onRejected(this.reason);
}
}
}
下一阶段我们就给这套基础状态机装上「回调篓子队列」,解决异步等待存任务的问题,完美适配真实延时外卖场景~
第二阶段:加装「回调篓子」完整版(衔接基础状态机,递进改造)
上一版只有状态机,处理不了异步延时——就像外卖要等一会才送到,你提前说了收货要做的事,总得先记下来,这两个数组 resolveQueue/rejectQueue 就是专门存事的「回调篓子」。
第2块:类骨架不变,构造器里新增两个回调篓子属性
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
// 原有基础状态不变
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// ========== 全新加装:两个回调篓子 ==========
// 成功篓子:存外卖没送到时,所有收货要做的事
this.resolveQueue = [];
// 失败篓子:存订单没取消前,所有失败兜底要做的事
this.rejectQueue = [];
}
}
改动说明:凭空新增两个数组,专门排队存待执行的回调函数,对应「先记下来,等送达再办」。
第3块:改造 resolve 函数——送达后自动清空执行成功篓子
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
// ========== 新增逻辑:外卖送到,挨个执行篓子里所有寄存的事 ==========
this.resolveQueue.forEach(fn => fn(this.value));
}
};
}
}
第4块:同步改造 reject 函数——订单取消清空执行失败篓子
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
this.resolveQueue.forEach(fn => fn(this.value));
}
};
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
// ========== 新增逻辑:订单取消,挨个执行失败篓子里寄存的事 ==========
this.rejectQueue.forEach(fn => fn(this.reason));
}
};
}
}
第6块:核心改造 then 方法——判断 pending,把回调塞进篓子
这是最关键改动:外卖还在做(pending),不执行,直接把事装篓子里排队
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
this.resolveQueue.forEach(fn => fn(this.value));
}
};
const reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
this.rejectQueue.forEach(fn => fn(this.reason));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
// 情况1:已经送到了,直接办收货的事
if (this.status === STATUS_FULFILLED ) {
onFulfilled(this.value);
}
// 情况2:已经取消了,直接办兜底的事
else if (this.status === STATUS_REJECTED ) {
onRejected(this.reason);
}
// ========== 全新核心逻辑:还在做饭 pending → 塞进对应篓子存起来 ==========
else if (this.status === STATUS_PENDING) {
this.resolveQueue.push(onFulfilled);
this.rejectQueue.push(onRejected);
}
}
}
加装回调篓子·阶段完整汇总代码
// 外卖三态常量
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
// 基础状态机属性
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// 两个回调篓子队列
this.resolveQueue = [];
this.rejectQueue = [];
// 送达开关 + 执行成功队列
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
this.resolveQueue.forEach(fn => fn(this.value));
}
};
// 取消开关 + 执行失败队列
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
this.rejectQueue.forEach(fn => fn(this.reason));
}
};
// 立刻执行做饭流程
executor(resolve, reject);
}
// 智能分发:已完成直接执行,pending就装篓子
then(onFulfilled, onRejected) {
if (this.status === STATUS_FULFILLED ) {
onFulfilled(this.value);
} else if (this.status === STATUS_REJECTED ) {
onRejected(this.reason);
} else if (this.status === STATUS_PENDING) {
this.resolveQueue.push(onFulfilled);
this.rejectQueue.push(onRejected);
}
}
}
第三阶段:刚需进阶版 基础链式调用(无边界裸奔版,核心骨架)
加装核心链式调用真正解决回调函数嵌套地狱
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
this.status = STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.rejectReason = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
this.resolveQueue.forEach(fn => fn(this.value));
}
};
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
this.rejectQueue.forEach(fn => fn(this.reason));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
// 核心刚需:返回新实例,实现链式接龙
return new _Promise((nextResolve, nextReject) => {
// 封装统一骑手任务:复用逻辑、可立即执行可入篓寄存、中转链式值
// 封装统一处理函数handleSuccess原因:
// 1.逻辑抽离复用,不用多处重复写判断/执行逻辑
// 2.包装成独立函数,既能立即执行,也可直接塞进队列寄存
// 3.中转结果交给下一个Promise,支撑链式流转
const handleSuccess = () => {
const res = onFulfilled(this.value);
nextResolve(res);
};
// 同成功处理逻辑:统一封装复用、可入队列、中转链式结果
const handleFail = () => {
const err = onRejected(this.reason);
nextResolve(err);
};
if (this.status === STATUS_FULFILLED) {
handleSuccess();
} else if (this.status === STATUS_REJECTED) {
handleFail();
} else if (this.status === STATUS_PENDING) {
this.resolveQueue.push(handleSuccess);
this.rejectQueue.push(handleFail);
}
});
}
}
刚需裸奔版存在核心问题(对应骑手配送漏洞)
1. 执行器executor报错直接崩程序(后厨做饭出事直接瘫痪,无应急)
2. then乱传非函数、空传省略回调直接报错(招了不会干活的假骑手,配送直接翻车)
3. 无值透传,中间空then直接断链式(中途骑手离岗,外卖没人接力送,链路废掉) 4. then内部回调自己报错无捕获(骑手送餐中途出事,全程没人兜底救援)
分步针对性边界优化(只改对应位置)
优化1:构造器加try/catch 兜底executor全局报错
只改constructor最后一行执行代码,其余全不变:
// 原有执行代码删掉,替换成下面
try {
executor(resolve, reject); // 正常执行商家出餐
} catch (err) {
reject(err); // 改动注释:后厨做饭报错直接转订单失败兜底,不崩系统
}
优化2:加函数校验+值透传 解决乱传/空传骑手断链问题
只改then里handle核心逻辑+队列存入判断:
const handleSuccess = () => {
// 改动注释:判断是不是正经骑手函数,不是就原值透传接力,不废单
const res = typeof onFulfilled === 'function' ? onFulfilled(this.value) : this.value;
nextResolve(res);
};
const handleFail = () => {
// 改动注释:失败回调同样校验+原因透传,保证坏单也能顺畅接力
const err = typeof onRejected === 'function' ? onRejected(this.reason) : this.reason;
nextResolve(err);
};
// 底部pending入队也改一行
if (this.status === STATUS_PENDING) {
// 改动注释:只把正经骑手存进任务篓,假骑手直接拒收不占用队列
typeof onFulfilled === 'function' && this.resolveQueue.push(handleSuccess);
typeof onRejected === 'function' && this.rejectQueue.push(handleFail);
}
优化3:内层try/catch 兜底骑手送餐中途自身报错(最终闭环)
只再包一层内部捕获:
const handleSuccess = () => {
// 改动注释:骑手干活中途出错即时救援,报错直接切失败单流转
try {
const res = typeof onFulfilled === 'function' ? onFulfilled(this.value) : this.value;
nextResolve(res);
} catch (err) {
nextReject(err);
}
};
const handleFail = () => {
try {
const err = typeof onRejected === 'function' ? onRejected(this.reason) : this.reason;
nextResolve(err);
} catch (err) { // 改动注释:失败处理出错同样兜底捕获,全链路无死角
nextReject(err);
}
};
最终完整完善版(直接阅读注释说明看到整个流程)
// 外卖订单三大固定状态:待接单/配送完成/订单拒收取消
const STATUS_PENDING = "pending";
const STATUS_FULFILLED = "fulfilled";
const STATUS_REJECTED = "rejected";
class _Promise {
constructor(executor = () => {}) {
// 初始化订单默认状态:全部处于待接单
this.status = STATUS_PENDING;
// 存放配送完成的餐品结果
this.value = undefined;
// 存放订单拒收的原因备注
this.reason = undefined;
// 骑手任务收纳篓:排队等候的配送任务/拒收善后任务
this.resolveQueue = [];
this.rejectQueue = [];
// 配送放行开关:只有待接单能改成完成,批量执行所有等候配送骑手
const resolve = (value) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED;
this.value = value;
this.resolveQueue.forEach(fn => fn(this.value));
}
};
// 订单拒收开关:只有待接单能改成取消,批量执行善后骑手任务
const reject = (reason) => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED;
this.reason = reason;
this.rejectQueue.forEach(fn => fn(this.reason));
}
};
// 后厨出餐容错:做饭翻车直接判订单失败,不瘫痪整个店铺
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 链式核心:每接一单就生成全新订单,实现骑手接力直送,完成异步时间扁平化
return new _Promise((nextResolve, nextReject) => {
// 统一封装正规配送骑手任务
const handleSuccess = () => {
// 骑手上岗核验+原值代送透传+送餐中途意外兜底
try {
const res = typeof onFulfilled === 'function' ? onFulfilled(this.value) : this.value;
nextResolve(res);
} catch (err) {
nextReject(err);
}
};
// 统一封装订单善后退款骑手任务
const handleFail = () => {
// 坏单兜底核验+原因透传+善后出错应急保护
try {
const err = typeof onRejected === 'function' ? onRejected(this.reason) : this.reason;
nextResolve(err);
} catch (err) {
nextReject(err);
}
};
// 根据订单当前状态调度骑手
if (this.status === STATUS_FULFILLED) {
handleSuccess(); // 已出餐直接派送
} else if (this.status === STATUS_REJECTED) {
handleFail(); // 已拒收直接善后
} else if (this.status === STATUS_PENDING) {
// 只收正经上岗骑手进任务篓,杂牌无效骑手直接拒收
typeof onFulfilled === 'function' && this.resolveQueue.push(handleSuccess);
typeof onRejected === 'function' && this.rejectQueue.push(handleFail);
}
});
}
}
整个流程结尾
我们全程用外卖订单+骑手配送的思路走完了手写Promise的主要核心流程:
最初先搭建核心骨架,定好订单三态状态机,搭配存放任务的回调篓,实现了最基础的异步任务寄存能力;
接着核心打通链式调用逻辑,每调用一次then就生成一张全新外卖订单,让骑手接力顺路配送,把嵌套混乱的回调地狱改成线性直行的流程,完成了Promise最关键的异步扁平化;
最后一步步叠加全套边界优化,用try/catch兜底后厨出餐报错、骑手送餐中途异常,用函数筛查过滤不会干活的假骑手,搭配值透传规则让空岗骑手原样代送不丢单,保障整条配送链路永远顺畅不崩。
至此我们就从最简裸奔版,迭代打磨出了逻辑完整、健壮可用的完整版基础Promise。至于 Promise.all 、 Promise.race 这类静态工具方法,只是在这套核心完备的订单系统之上,额外封装的组合派单简易逻辑,属于锦上添花的拓展用法,不改动我们底层状态机、队列调度、链式流转的核心架构,这里就不再额外展开赘述了。
如有理解不当,欢迎大家指正,一起学习进步