手写 promise 是前端面试过程中的必考题,今天来温习下手写 promise 过程并写下思考过程,熟悉思路。
基本框架
在 promise 的日常使用中我们经常会看到以下类似代码
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 300);
});
myPromise.then(handleFulfilledA, handleRejectedA);
根据以上使用方式我们知道 Promise 是一个构造函数,且有一个函数类型参数 executor,这个函数参数是会立即执行的,且这个函数拥有 resolve 和 reject 这两个函数类型参数。且 Promise 的对象存在一个 then 方法,这个 then 方法里面会有两个参数,一个是成功的回调 onFulfilled,另一个是失败的回调 onRejected,调用 resolve 会执行 onFulfilled,调用 reject 会执行 onRejected。这个方法需要添加在原型上。至此我们可以写出 promise 基本框架
class MyPromise {
constructor(executor) {
const resolve = () => {};
const reject = () => {};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {}
}
三种状态
我们知道 promise 中文翻译为承诺,即状态不会轻易改变,其状态只能从 pending 变为 fulfilled 或者从 pending 变为 rejected。resolve 函数执行说明状态变为 fulfilled,reject 函数执行说明状态变为 rejected
故此我们可以完善这三种状态
const STATUS = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING; // 初始状态
this.value = void 0; // promise成功执行的结果
this.reason = void 0; // promise被拒绝的原因
const resolve = (value) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value; // 将结果保存下来方便在onFulfilled回调中使用
}
};
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) {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
onFulfilled(this.value);
} else if (this.status === STATUS.REJECTED) {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
onRejected(this.reason);
}
}
}
至此一个简易版的 promise 完成了,我们可以来试用下
const p1 = new MyPromise((resolve, reject) => {
resolve("success");
});
p1.then(
(res) => {
console.log("res", res); // res success
},
(err) => {
console.log("err", err);
}
);
添加异步
上面简易版的 MyPromise 中只能执行同步方法,当其中包含异步调用的时候,then 方法直接跳过了。因为在异步调用中执行 then 方法的时候,状态还未改变仍旧是 pending。等到异步的 Promise 状态发生改变的时候,then 已经执行完了。这里需要改造下 then 方法
const STATUS = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING; // 初始状态
this.value = void 0; // promise成功执行的结果
this.reason = void 0; // promise被拒绝的原因
this.onResolvedCallbacks = []; // 存放所有成功的回调。
this.onRejectedCallbacks = []; // 存放所有失败的回调。
const resolve = (value) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value; // 将结果保存下来方便在onFulfilled回调中使用
this.onResolvedCallbacks.forEach((fn) => fn()); // 将存储的onFulfilled依次执行
}
};
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn()); // 将存储的onRejected依次执行
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if (this.status === STATUS.FULFILLED) {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
onFulfilled(this.value);
} else if (this.status === STATUS.REJECTED) {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
onRejected(this.reason);
} else {
// 状态仍旧是pending
// 这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
添加异步调用试试看结果
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
});
p1.then(
(res) => {
console.log("res", res); // res success
},
(err) => {
console.log("err", err);
}
);
链式调用
我们知道 promise 能够解决回调地狱的根本原因是因为它支持链式调用.
如果一个 then 方法返回普通值,这个值会传递给下一次 then 中,作为成功的结果。
如果返回的是一个 promise,则会把 promise 的执行结果传递下去,并且取决于这个 Promise 的成功或失败。
如果返回的是一个报错就会执行到下一个 then 的失败的函数中。
Promise 的 then 方法返回的是一个全新的 Promise,而不是当前的 Promise。因为 Promise 的状态只能改变一次,如果使用同一个 Promise 的话后面的 then 执行时状态就不能变了。
这里我们来修改下 then 方法
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING; // 初始状态
this.value = void 0; // promise成功执行的结果
this.reason = void 0; // promise被拒绝的原因
this.onResolvedCallbacks = []; // 存放所有成功的回调。
this.onRejectedCallbacks = []; // 存放所有失败的回调。
const resolve = (value) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value; // 将结果保存下来方便在onFulfilled回调中使用
this.onResolvedCallbacks.forEach((fn) => fn()); // 将存储的onFulfilled依次执行
}
};
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn()); // 将存储的onRejected依次执行
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
//解决 onFulfilled,onRejected 没有传值的问题
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: function (err) {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
//前一个then方法的返回值会传递给下一个then
try {
const x = onFulfilled(this.value); // 这里可能返回promise
resolve(x);
} catch (e) {
// 执行失败需要reject抛出错误
reject(e);
}
} else if (this.status === STATUS.REJECTED) {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
try {
const x = onRejected(this.reason);
resolve(x);
} catch (e) {
reject(e);
}
} else {
// 状态仍旧是pending
// 这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected
this.onResolvedCallbacks.push(() => {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
}
可以尝试下链式调用
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
});
p1.then().then((res) => {
console.log("res", res); //res success
});
通过Promise A+ 规范 了解到这里 then 方法中 onFulfilled(this.value)可能返回 promise,这时候需要写一个方法来判断,如果返回值是 promise 就调用 promise,否则才继续向 resolve 传递。
这里定义一个 resolvePromise 方法,在函数中判断返回值 x 和 promise2 的关系以及后续的处理,所以需要传递 promise2 参数,x 参数,resolve 参数和 reject 参数。
function resolvePromise(promise2, x, resolve, reject) {
//首先判断promise2和x引用了同一个promise对象
if (promise2 === x) {
// 防止自己等待自己
return reject(new TypeError("循环引用了"));
}
let called; //表示Promise有没有被调用过 确保只调用一次
// x是object或者是个function
if ((x !== null && typeof x === "object") || typeof x === "function") {
try {
let then = x.then;
//如果then是个函数,就认为他是Promise, 需要通过call执行then方法,改变this的指向为x,then中传入成功和失败的函数,官方文档中指明成功函数的参数叫y,失败的参数为r
if (typeof then === "function") {
then.call(
x,
function (y) {
if (called) {
// 是否调用过
return;
}
called = true;
// y有可能也是一个Promise,所以不能直接写resolve(y),应该递归判断y和promise2的关系。需要调用resolvePromise
resolvePromise(promise2, y, resolve, reject);
},
function (r) {
if (called) {
return;
}
called = true;
reject(r);
}
);
}
} catch (e) {
if (called) {
return;
}
called = true;
reject(e);
}
} else {
if (called) {
return;
}
called = true;
resolve(x);
}
}
class MyPromise {
// ...
then(onFulfilled, onRejected) {
//解决 onFulfilled,onRejected 没有传值的问题
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: function (err) {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
//前一个then方法的返回值会传递给下一个then
try {
const x = onFulfilled(this.value); // 这里可能返回promise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// 执行失败需要reject抛出错误
reject(e);
}
} else if (this.status === STATUS.REJECTED) {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
// 状态仍旧是pending
// 这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected
this.onResolvedCallbacks.push(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
}
promise.then 异步
我们知道 promise.then 会创建一个异步的微任务,这里我们用 queueMicrotask()来创建,改造下 then 方法
class MyPromise {
// ...
then(onFulfilled, onRejected) {
//解决 onFulfilled,onRejected 没有传值的问题
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: function (err) {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
queueMicrotask(() => {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
//前一个then方法的返回值会传递给下一个then
try {
const x = onFulfilled(this.value); // 这里可能返回promise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// 执行失败需要reject抛出错误
reject(e);
}
});
} else if (this.status === STATUS.REJECTED) {
queueMicrotask(() => {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else {
// 状态仍旧是pending
// 这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
}
补充 catch 和 finally
catch 其实就是 then 方法失败回调
finally:finally 表示不是最终的意思,而是无论如何都会执行的意思。
如果返回一个 promise 会等待这个 promise 也执行完毕。如果返回的是成功的 promise,会采用上一次的结果;如果返回的是失败的 promise,会用这个失败的结果,传到 catch 中。
class MyPromise {
//...
catch(onRejected) {
return this.then(null, onRejected);
},
finally(callback) {
return this.then(
(value) => {
return MyPromise.resolve(callback()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
}
}
添加静态方法 resolve reject all race allSettled
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING; // 初始状态
this.value = void 0; // promise成功执行的结果
this.reason = void 0; // promise被拒绝的原因
this.onResolvedCallbacks = []; // 存放所有成功的回调。
this.onRejectedCallbacks = []; // 存放所有失败的回调。
const resolve = (value) => {
// 如果 value 是一个promise,那也要实现一个递归解析
if (value instanceof Promise) {
// 递归解析
return value.then(resolve, reject);
}
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value; // 将结果保存下来方便在onFulfilled回调中使用
this.onResolvedCallbacks.forEach((fn) => fn()); // 将存储的onFulfilled依次执行
}
};
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn()); // 将存储的onRejected依次执行
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// ...
static resolve(data) {
return new MyPromise((resolve, reject) => {
resolve(data); // 产生一个成功的 promise
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}
promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。
class MyPromise {
// ...
static all(promises) {
// 判断参数是否可遍历
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === promises.length) {
resolve(resultArr);
}
};
for (let i = 0; i < promises.length; i++) {
let value = promises[i];
if (value && typeof value.then === "function") {
// value是promise
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
}
}
Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。
class MyPromise {
// ...
static race(promises) {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let value = promises[i];
if (value && typeof value.then === "function") {
value.then(resolve, reject);
} else {
resolve(value);
}
}
});
}
}
Promise.allSettled()方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。
class MyPromise {
// ...
allSettled(promises) {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise(function (resolve) {
const len = promises.length;
const results = [];
let count = 0;
promises.forEach((s, index) => {
MyPromise.resolve(s)
.then((res) => {
results[index] = {
status: "fulfilled",
value: s,
};
if (++count === len) {
resolve(results);
}
})
.catch((val) => {
results[index] = {
status: "reject",
value: s,
};
count++;
if (count === len) {
resolve(results);
}
});
});
});
}
}
最终完整 MyPromise
const STATUS = {
PENDING: "pending",
FULFILLED: "fulfilled",
REJECTED: "rejected",
};
/**
* 判断x是否是promise2,如果是promise2就执行并且将执行结果添加到resolve方法中,如果是常量则直接添加到resolve方法中
* @param {*} promise2
* @param {*} x
* @param {*} resolve
* @param {*} reject
*/
function resolvePromise(promise2, x, resolve, reject) {
//首先判断promise2和x引用了同一个promise对象
if (promise2 === x) {
// 防止自己等待自己
return reject(new TypeError("循环引用了"));
}
let called; //表示Promise有没有被调用过 确保只调用一次
// x是object或者是个function
if ((x !== null && typeof x === "object") || typeof x === "function") {
try {
let then = x.then;
//如果then是个函数,就认为他是Promise, 需要通过call执行then方法,改变this的指向为x,then中传入成功和失败的函数,官方文档中指明成功函数的参数叫y,失败的参数为r
if (typeof then === "function") {
then.call(
x,
function (y) {
if (called) {
// 是否调用过
return;
}
called = true;
// y有可能也是一个Promise,所以不能直接写resolve(y),应该递归判断y和promise2的关系。需要调用resolvePromise
resolvePromise(promise2, y, resolve, reject);
},
function (r) {
if (called) {
return;
}
called = true;
reject(r);
}
);
}
} catch (e) {
if (called) {
return;
}
called = true;
reject(e);
}
} else {
if (called) {
return;
}
called = true;
resolve(x);
}
}
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = void 0;
this.reason = void 0;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
// promise.resolve 是具备等待功能的。
// 如果参数是 promise 会等待这个 promise 解析完毕,在向下执行
if (value instanceof MyPromise) {
// 递归解析
return value.then(resolve, reject);
}
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
//解决 onFulfilled,onRejected 没有传值的问题
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: function (err) {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
// 当状态变为fulfilled时 执行onFulfilled回调,且传入相应的值
//前一个then方法的返回值会传递给下一个then
try {
const x = onFulfilled(this.value); // 这里可能返回promise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// 执行失败需要reject抛出错误
reject(e);
}
} else if (this.status === STATUS.REJECTED) {
// 当状态变为rejected时 执行onRejected回调,且传入相应的值
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
// 状态仍旧是pending
// 这个时候应该把onFulfilled和onRejected先存起来,当执行了resolve或者reject的时候再执行onFulfilled或onRejected
this.onResolvedCallbacks.push(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(callback) {
return this.then(
(value) => {
return MyPromise.resolve(callback()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
}
static resolve(data) {
return new MyPromise((resolve, reject) => {
resolve(data);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === promises.length) {
resolve(resultArr);
}
};
for (let i = 0; i < promises.length; i++) {
let value = promises[i];
if (value && typeof value.then === "function") {
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
}
static race(promises) {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let value = promises[i];
if (value && typeof value.then === "function") {
value.then(resolve, reject);
} else {
resolve(value);
}
}
});
}
static allSettled(promises) {
if (!Array.isArray(promises)) {
const type = typeof promises;
return new TypeError(`TypeError: ${type} ${promises} is not iterable`);
}
return new MyPromise(function (resolve) {
const len = promises.length;
const results = [];
let count = 0;
promises.forEach((s, index) => {
MyPromise.resolve(s)
.then((res) => {
results[index] = {
status: "fulfilled",
value: s,
};
if (++count === len) {
resolve(results);
}
})
.catch((val) => {
results[index] = {
status: "reject",
value: s,
};
count++;
if (count === len) {
resolve(results);
}
});
});
});
}
}
参考文章:
手写 Promise 源码,再深入理解 Promise 机制
原文链接: https://jesse121.github.io/blog/articles/2022/09/10.html
版权声明: 转载请注明出处.