在 JavaScript 编程中,异步操作是一个至关重要的概念。随着 Web 应用的复杂性不断增加,处理异步任务变得愈发常见。Promise 作为一种处理异步操作的强大工具,在 JavaScript 开发者的日常工作中扮演着不可或缺的角色。不仅在工作,在面试中,不管是 大厂还是小厂,手写Promise已经基本成为面试的必考点,下面就让我们一起去了解Promise。
想要手写一个Promise,就必须要了解Promise A+规范,目前所有的Promise类库,都要遵守这个规范。
先写一个基础的Promise
new Promise 时需要传递一个执行器executor()
,执行器会立即执行。
Promise
class CustPromise1 {
constructor(executor){
executor()//立即执行
};
};
const cPromise1 = new CustPromise1((resolve, reject)=>{
console.log("手撕Promise");
});
为Promise添加resolve,reject 以及状态
- promise有三个状态:
pending
,fulfilled
,rejected
,分别是:等待, 完成, 拒绝。 - 执行器需要接收两个参数:
resolve
,reject
。 - Promise的默认状态:
pending
。
代码中我们分别定义三个常量来存储不同的状态:PROMISE_STATUS_PENDING:pending, PROMISE_STATUS_FULFILLED: fulfilled, PROMISE_STATUS_REJECTED:rejected。
当执行resolve或者reject方法回调时,必须要先判断当前的状态是否是pending
,只有状态为初始状态时才可以执行。
CustPromise2
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
class CustPromise2 {
constructor(executor){
// 默认状态
this.status = PROMISE_STATUS_PENDING;
const resolve = ()=>{
// 只有初始状态才允许执行,状态发生变化则锁死不执行
if(this.status === PROMISE_STATUS_PENDING){
// 改变状态:fulfilled
this.status = PROMISE_STATUS_FULFILLED;
console.log("Promise.resolve 被调用!")
};
};
const reject = ()=>{
// 只有初始状态才允许执行,状态发生变化则锁死不执行
if(this.status === PROMISE_STATUS_PENDING){
// 改变状态
this.status = PROMISE_STATUS_REJECTED;
console.log("Promise.reject 被调用!")
};
};
executor(resolve, reject);
};
};
const cPromise2 = new CustPromise2((resolve, reject)=>{
console.log("pending状态");
resolve(); // resolve 被调用后 初始状态发生改变 reject便不会再被调用,反之也如此。
reject();
});
实现异步信息传递
异步信息的传递,离不开resolve,reject 方法去接收异步信息。因此,Promise需要有一个变量value
用来保存成功状态的值,需要一个reason
用来保存失败状态的值。
CustPromise3
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
class CustPromise3{
constructor(executor){
this.status = PROMISE_STATUS_PENDING;
// 声明两个用于接收异步信息的变量
this.value = undefined;
this.reason = undefined;
const resolve = (value)=>{
// 只有初始状态才允许执行,状态发生变化则锁死不执行
if(this.status === PROMISE_STATUS_PENDING){
// 改变状态
this.status = PROMISE_STATUS_FULFILLED;
// 赋值异步信息
this.value = value;
console.log("resolve被调用")
};
};
const reject = (reason)=>{
// 只有初始状态才允许执行,状态发生变化则锁死不执行
if(this.status === PROMISE_STATUS_PENDING){
// 改变状态
this.status = PROMISE_STATUS_REJECTED;
// 赋值对应拒绝信息
this.reason = reason;
console.log("reject被调用")
};
};
executor(resolve, reject);
};
};
const cPromise3 = new CustPromise3((resolve, reject)=>{
console.log("pending");
resolve("success"); // resolve 被调用后 初始状态发生改变 reject便不会再被调用,反之也如此。
reject("fail");
});
添加then方法,以及对应的执行顺序
Promise必须有一个then
方法,该方法接收两个参数:onFulfilled
,onRejected
。
- onFulfilled:调用then方法时,若执行成功,就执行onFulfilled方法,参数是Promise的value。
- onRejected:调用then方法时,若执行失败,就执行onRejected方法,参数是Promise的reason。
CustPromise4
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECDED = "rejected";
class CustPromise4 {
constructor(executor){
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
// this.onFulfilled(); //then方法接收resolve状态传递的信息
// setTimeout(()=>this.onFulfilled(), 0);
queueMicrotask(() => this.onFulfilled(value));
console.log("resolve被调用");
};
};
const reject = (reason)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_REJECDED;
this.reason = reason;
// this.onRejected(); //then方法接收reject状态传递的信息
queueMicrotask(() => this.onRejected(reason));
console.log("reject被调用")
};
};
executor(resolve, reject);
};
// 编写then方法
then(onFulfilled, onReject){
this.onFulfilled = onFulfilled;
this.onRejected = onReject;
};
};
const cPromise4 = new CustPromise4((resolve, reject)=>{
console.log("pending");
resolve("success"); // resolve 被调用后 初始状态发生改变 reject便不会再被调用,反之也如此。
reject("fail");
});
cPromise4.then((res)=>{
console.log("res", res);
},
(err)=>{
console.log("err", err);
},
);
如上代码所示 ,在resolve和reject方法执行时会调用 then
方法中提供两个方法,但是在调用Promise时,执行器executor
里的内容会被立即执行。但是JS引擎是从上到下 的执行顺序,调用CustPromise4实例对象的then方法是注定会在new该实例对象的后面,所以会出现报错onFulfilled/onRejected is not function
。
如何解决上述问题呢?此刻我们可以引用Window
中提供的queueMicrotask
方法来处理。如果不想使用此方法,也可通过在then方法中传入参数的方式处理,如下所示:
CustPromise4
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECDED = "rejected";
class CustPromise4 {
constructor(executor){
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
console.log("resolve被调用");
};
};
const reject = (reason)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_REJECDED;
this.reason = reason;
console.log("reject被调用")
};
};
executor(resolve, reject);
};
// 编写then方法
then(onFulfilled, onReject){
if(this.status ===PROMISE_STATUS_FULFILLED){
onFulfilled(this.value);
};
if(this.status ===PROMISE_STATUS_REJECDED){
onReject(this.reason);
};
};
};
const cPromise4 = new CustPromise4((resolve, reject)=>{
console.log("pending");
resolve("success"); // resolve 被调用后 初始状态发生改变 reject便不会再被调用,反之也如此。
reject("fail");
});
cPromise4.then((res)=>{
console.log("res", res);
},
(err)=>{
console.log("err", err);
},
);
对then方法持续优化,以及链式调用
CustPromise6
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECDED = "rejected";
// 工具函数
function custPromiseCallbackErr(executor, value, resolve, reject) {
try {
const result = executor(value)
resolve(result)
} catch(err) {
reject(err)
}
}
class CustPromise6 {
constructor(executor){
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
// 存放成功的回调
this.onResolvedCallbacks = [];
// 存放失败的回调
this.onRejectedCallbacks = [];
const resolve = (value)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_FULFILLED;
this.value = value;
queueMicrotask(()=>{
if (this.status !== PROMISE_STATUS_PENDING) return
this.onResolvedCallbacks.forEach(fn=>fn(this.value))
}); // 对累积的then调用兑现状态函数进行遍历分别调用
};
};
const reject = (reason)=>{
if(this.status === PROMISE_STATUS_PENDING){
this.status = PROMISE_STATUS_REJECDED;
this.reason = reason;
queueMicrotask(()=>{
if (this.status !== PROMISE_STATUS_PENDING) return
this.onRejectedCallbacks.forEach(fn=>fn(this.reason))
}); // 对累积的then调用拒绝状态函数进行遍历分别调用
};
};
try {
executor(resolve,reject)
} catch (error) {
reject(error)
}
};
// then方法
// 将成功/失败的回调方法放入数组中 进行统一调用。
then(onFulfilled, onReject){
return new CustPromise6((resolve,reject)=>{
// 如果在then调用的时候, 状态已经确定下来
if(this.status === PROMISE_STATUS_FULFILLED && onFulfilled){
custPromiseCallbackErr(onFulfilled, this.value, resolve,reject);
};
// 如果在then调用的时候, 状态已经确定下来
if(this.status === PROMISE_STATUS_REJECDED && onReject){
custPromiseCallbackErr(onFulfilled, this.reason, resolve,reject);
};
// 将成功回调和失败的回调放到数组中
if(this.dstatus === PROMISE_STATUS_PENDING){
this.onResolvedCallbacks.push(()=>{
custPromiseCallbackErr(onFulfilled, this.reason, resolve,reject);
});
this.onRejectedCallbacks.push(()=>{
custPromiseCallbackErr(onReject, this.reason, resolve,reject);
});
};
});
};
};
const cPromise6 = new CustPromise6((resolve, reject)=>{
console.log("状态pending")
resolve('成功调用resolve')
});
cPromise6.then(res => {
console.log("res1:", res)
return "11111"
}, err => {
console.log("err1:", err)
return "2222"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
});