前言
平时一直没想过手写 Promise,结果今天笔试初见,绞尽脑汁才写了个简易版本,还是菜了,头被锤烂
其实到了真正手写的时候才发现,自己对 Promise 根本就一无所知。我看你是完全不懂喔
回调函数的执行顺序?怎么实现链式调用?返回值穿透?延迟绑定?resolve、reject 做了什么?then 中的回调函数返回值怎么处理?哪些是同步?哪些是异步?
搞清楚这些问题,才能算是熟悉 Promise了。
代码实现
首先,根据 Promise/A+
规范,一个 Promise 应该具有三种状态,并且同一时刻只能为其一,状态变更后,不可回退:
const STATUS = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
一开始 Promise 的状态应该是 pending。
先回忆一下 new Promise
的使用:
let p = new Promise((resolve, reject) => {});
它接收一个回调函数 executor
,具有两个函数类型的参数:resolve、reject
。
在构造函数初始化 Promise 实例时,执行 executor
,传入构造函数中定义的 resolve、reject
作为其参数。
在 executor
回调函数中执行 resolve、reject
回调,即可将该 Promise 的状态改变,並异步触发将 then 传入的回调加入微任务队列。因此,用一个回调队列来模拟微任务队列。
value
用来保存 resolve、reject
传入的参数。
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = null;
this.resolveCallbacks = [];
this.rejectCallbacks = [];
const resolve = (value) => {};
const reject = (reason) => {};
try {
// new Promise()时立即执行executor,并传入resolve和reject
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
再想想 resolve、reject
的实现:
仅当 Promise 的状态为 pending 时,可以被调用,改变状态并将参数作为 Promise 的 value
。
在 resolve/reject
后,异步执行 then 中传入的回调函数(如果存在多个,依次执行)。
const resolve = (value) => {
const run = () => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
// 储存当前值,用于then回调
this.value = value;
while (this.resolveCallbacks.length) {
// 一次取一个回调执行
const cb = this.resolveCallbacks.shift();
// 等待该Promise的所有then回调保持一致
cb(value);
}
}
};
// 模拟异步,规范上是微任务,这里是宏任务
setTimeout(run);
};
const reject = (reason) => {
const run = () => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.value = reason;
while (this.rejectCallbacks.length) {
const cb = this.rejectCallbacks.shift();
cb(value);
}
}
};
setTimeout(run);
};
接下来是最核心的 then 函数,也是链式调用、返回值穿透、延迟绑定的具体实现:
then 函数本身是同步执行的,而其中的回调函数并不会立即执行,即使在调用 then 注册回调函数的时候 Promise 对象已经是确定的状态,Promise 也会以异步的方式调用该回调函数(延迟绑定)。
如果上一个 Promise 状态不为 pending,将 then 中的回调函数加入微任务队列(这里模拟不了微任务,故用异步宏任务的 resolve/reject
代替,回调入队的逻辑也要发生变化,即 pending 时入队)。在当前宏任务执行完后,依次执行至微任务队列清空。
then 的返回值是一个全新的 Promise(链式调用的原理)。
如果 then 中的回调函数没有返回值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined
。
如果 then 中的回调函数返回值为另一个 Promise,等待其状态变更。否则,直接 resolve,并且将返回的值作为接受状态的回调函数的参数值。
如果 then 中的回调函数抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
如果参数不是函数类型,将 value 原封不动地往下传(返回值穿透)。
// then 函数本身是同步执行
then(onfulfilled, onrejected) {
typeof onfulfilled !== "function" ? (onfulfilled = (value) => value) : null;
typeof onrejected !== "function" ? (onrejected = (error) => error) : null;
return new MyPromise((resolve, reject) => {
// 成功回调,resolve 时加入微任务队列
const resolveFn = (value) => {
try {
const x = onfulfilled(value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
};
// 失败回调,reject 时加入微任务队列
const rejectFn = (error) => {
try {
const x = onrejected(error);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};
switch (this.status) {
case STATUS.PENDING:
this.resolveCallbacks.push(resolveFn);
this.rejectCallbacks.push(rejectFn);
break;
case STATUS.FULFILLED:
resolveFn(this.value);
break;
case STATUS.REJECTED:
rejectFn(this.value);
break;
}
});
}
}
至此,一个基本的 Promise 就完成了:
const STATUS = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = undefined;
this.resolveCallbacks = [];
this.rejectCallbacks = [];
const resolve = (value) => {
const run = () => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
// 储存当前值,用于then回调
this.value = value;
// 一次取一个回调执行
while (this.resolveCallbacks.length) {
const cb = this.resolveCallbacks.shift();
// 等待该Promise的所有then回调保持一致
cb(value);
}
}
};
// 模拟异步,规范上是微任务,这里是宏任务
setTimeout(run);
};
const reject = (reason) => {
const run = () => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.value = reason;
while (this.rejectCallbacks.length) {
const cb = this.rejectCallbacks.shift();
cb(value);
}
}
};
setTimeout(run);
};
// new Promise()时立即执行executor,并传入resolve和reject
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then函数本身是同步执行
then(onfulfilled, onrejected) {
// 根据规范,如果then的参数不是function,则忽略它, 让值继续往下传递,链式调用继续往下执行
typeof onfulfilled !== "function" ? (onfulfilled = (value) => value) : null;
typeof onrejected !== "function" ? (onrejected = (error) => error) : null;
// then 返回一个新的promise
return new MyPromise((resolve, reject) => {
// 成功回调,resolve 时加入微任务队列
const resolveFn = (value) => {
try {
const x = onfulfilled(value);
// 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
};
// 失败回调,reject 时加入微任务队列
const rejectFn = (error) => {
try {
const x = onrejected(error);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};
// 根据上一个 Promise 当前状态
switch (this.status) {
case STATUS.PENDING:
// 回调函数加入回调任务队列
this.resolveCallbacks.push(resolveFn);
this.rejectCallbacks.push(rejectFn);
break;
// 模拟中resolve为异步宏任务,所以不会进入
case STATUS.FULFILLED:
resolveFn(this.value);
break;
case STATUS.REJECTED:
rejectFn(this.value);
break;
}
});
}
catch(rejectFn) {
return this.then(undefined, rejectFn);
}
}
然后是一些 Promise 相关的 API:
static resolve(value) {
if (value instanceof myPromise) return value;
if (value === null)
return new myPromise((resolve, reject) => resolve(null));
if (typeof value === "object" || typeof value === "function") {
try {
// 如果是thenable对象
let then = value.then;
if (typeof then === "function") {
// 返回的promise的状态跟随then执行
return new myPromise(then.bind(value));
}
} catch (e) {
return new myPromise((resolve, reject) => reject(e));
}
}
return new myPromise((resolve, reject) => resolve(value));
}
static reject(reason) {
return new myPromise((resolve, reject) => {
reject(reason);
});
}
static finally(callback) {
return this.then(
(value) => {
// callback是异步回调,则等待其执行
return myPromise.resolve(callback()).then(() => value);
},
(error) => {
return myPromise.reject(callback()).then(() => error);
}
);
}
static all(promises) {
return new myPromise((resolve, reject) => {
// 必须是可迭代对象
if (
!promises ||
typeof promises !== "object" ||
typeof promises[Symbol.iterator] !== "function"
) {
reject(new TypeError());
}
let count = 0;
const result = [];
if (promises.length === 0) return resolve([]);
for (let i = 0; i < promises.length; i++) {
myPromise.resolve(promises[i]).then(
(value) => {
count++;
result[i] = value;
if (count === promises.length) {
resolve(result);
}
},
(error) => reject(error)
);
}
});
}
static race(promises) {
return new myPromise((resolve, reject) => {
// 必须是可迭代对象
if (
!promises ||
typeof promises !== "object" ||
typeof promises[Symbol.iterator] !== "function"
) {
reject(new TypeError(""));
}
if (promises.length === 0) return;
for (let i = 0; i < promises.length; i++) {
myPromise.resolve(promises[i]).then(
(value) => resolve(value),
(error) => reject(error)
);
}
});
}
static allSettled(promises) {
return new myPromise((resolve, reject) => {
// 必须是可迭代对象
if (
!promises ||
typeof promises !== "object" ||
typeof promises[Symbol.iterator] !== "function"
) {
reject(new TypeError(""));
}
if (promises.length === 0) resolve([]);
let count = 0;
const result = new Array(promises.length);
for (let i = 0; i < promises.length; i++) {
myPromise.resolve(promises[i]).then(
(value) => {
result[i] = { status: "fulfilled", value: value };
count++;
if (count === promises.length) {
resolve(result);
}
},
(reason) => {
result[i] = { status: "rejected", reason: reason };
count++;
if (count === promises.length) {
resolve(result);
}
}
);
}
});
}
}
- Post link: https://blog.sticla.top/2021/09/24/es6-promise/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
GitHub Issues