前言
手写 Promise 是面试的时候大家都逃避的送命题,在学些了解后发现通过实现源码更能将新一代的异步方案理解的通透,知其然知其所以然的运用。
如果直接将源码贴到此处势必不能有更大的收获,下面就按实现版本来看做简要分析。
回顾 Promise
Promise 是 CommonJS 提出来的这一种规范,有多个版本,在 ES6 当中已经纳入规范,原生支持 Promise 对象,非 ES6 环境可以用类似 Bluebird、Q 这类库来支持。
Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅,还可以批量处理异步任务。
简单归纳下 Promise:三个状态、两个过程、一个方法,快速记忆方法:3-2-1
三个状态:pending、fulfilled、rejected
两个过程:
- pending → fulfilled(resolve)
- pending → rejected(reject)
一个方法:then
当然还有其他概念,如 catch、 Promise.all/race/allSettled。
基础版
基础测试用例
// 1. 链式调用
var p1 = new Promise(function (resolve, reject) {console.log("init Promise");if (Math.random() > 0.5) {resolve("大");} else {reject("小");}
});
p1.then((data) => console.log("success", data),(reason) => console.log("error", reason)
).then(() => console.log("success 2"),() => console.log("error 2")
);
// 2. 异步延时
var sleep = (time, data) =>new Promise(function (resolve, reject) {setTimeout(resolve, time, data);});
sleep(3000, "时间到!").then((val) => {console.log(val);
});
// 3. 状态变更后不可变
const p2 = new Promise(function (resolve, reject) {resolve("失败了!");reject("还会成功吗!");
});
p2.then((data) => console.log(data),(reason) => console.log(reason)
);
// Promise 打印日志:
// init Promise
// success 大 / error 小
// 失败了!
// success 2 /error 2
// 时间到!(延时 3 s)
Promise 的基本特征
1.new promise 时, 需要传入一个立即执行函数 fn,fn 接受两个参数,分别是 resolve 和 reject;
2.promise 有三个状态:pending,fulfilled,or rejected, 默认状态是 pending,只能从 pending 到 rejected, 或者从 pending 到 fulfilled,状态一旦确认,就不会再改变;
3.promise 必须有一个 then 方法,then 接收两个参数,分别是 promise 成功的回调 fulfilledFn, 和 promise 失败的回调 rejectedFn;
4.promise then 支持链式调用。
手写基础版
class Promise {constructor(executor) {this.status = "pending";this.handleFulfilled = []; // 存储成功后的回调this.handleRejection = []; // 存储失败后的回调// ! resolve 形参的实际参数在这儿const resolve = (data) => {// 状态变更只有一次if (this.status !== "pending") {return;}this.status = "fulfilled";// ! 等一会,否则 handleFulfilled 为空setTimeout(() => {this.handleFulfilled.forEach((fn) => fn(data));}, 0);};const reject = (reason) => {if (this.status !== "pending") {return;}this.status = "rejected";setTimeout(() => {this.handleRejection.forEach((fn) => fn(reason));}, 0);};try {executor(resolve, reject);} catch (e) {// 遇到错误时,捕获错误,执行 reject 函数reject(e);}}then(fulfilledFn, rejectedFn) {this.handleFulfilled.push(fulfilledFn);this.handleRejection.push(rejectedFn);return this;}
}
测试用例:
// 简易版 Promise 打印日志:
// init Promise
// success 大 / error 小
// success 2 /error 2 (x 未通过)
// 失败了!
// 时间到!(延时 3 s)
存在的问题:
1.微任务宏任务队列打印顺序
2.then 链式调用不是通过返回 this,而是返回一个新的 promise
3.链式调用支持参数缺省
4.等等…
认识 Promise /A+ 规范
手写 Promise,需要遵守怎样的规则,业界所有 Promise 的类库都遵循 Promise/A+ 规范。译文
改进版
先按 Promise 的基本规范对上面的基础版进行改进。
1.new promise 时, 需要传入一个立即执行函数executor
,executor
接受两个参数,分别是 resolve 和 reject;
2.promise 有三个状态:pending
,fulfilled
,or rejected
, 默认状态是 pending
,只能从 pending
到 rejected
, 或者从 pending
到 fulfilled
,状态一旦确认,就不会再改变;「规范 Promise/A+ 2.1」
3.promise 有一个value
保存成功状态的值,可以是undefined/thenable/promise
;「规范 Promise/A+ 1.3」
4.promise 有一个reason
保存失败状态的值;「规范 Promise/A+ 1.5」
5.promise 必须有一个 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected