开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
前言
关于Promise,想必大家并不陌生,使用起来比较方便,我们也是在各种项目中见到它越来越多的身影。同时,手写promise也成为各种面试官必问的一道考点,它也几乎成了各位前端人必须掌握的技能能之一。网上有很多优秀的手写promise的文章,但是学习这种事嘛,只有自己写下来才是完全掌握,所以,我才写下这篇文章。
Promise优点
Promise之所以能够广泛地被使用,肯定是它有着别人难以替代的魅力,在之后我们可能会这样处理一些异步操作。
ajax.get({ url: 'xxx' }, (data1) => {
ajax.get({ url: 'xxxx', data: data1 }, (data2) => {
ajax.get({ url: 'xxx', data: data2 }, (data3) => {
...
});
});
});
下一个接口的入参依赖上一个接口的返回值,我们只能通过这样不断嵌套的方式,这也就是我们常说的回调地狱的问题。而Promise的出现,它提供链式调用的方式方便我们处理返回结果,我们也可以配合async awit的使用,来更好的处理异步操作。好了,我们废话少说,直接实现。
简单实现
实现标准
我们实现的Promise必须满足PromiseA+规范,标准的Promise也是基于此标准实现。中文翻译。
- promise 的状态
pormise 必须是以下三个状态之一: pending, fulfilled, rejected.
- then 方法
promise 必须提供一个 then 方法,能由此去访问当前或最终的 value 或者 reason 。
这里我们标注出我们Promise的要求,我们实现的标准都应该在此基础上。
基础实现
// Promise的基础状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
this.status = PENDING; // 默认的pending状态
const resolve = (value) => {
this.value = value;
this.status = FULFILLED;
};
const reject = (reason) => {
this.reason = reason;
this.status = REJECTED;
};
try {
executor(resolve, reject);
} catch (e) {
// 如果执行时发生了异常 那么这个异常就是失败的原因
reject(e);
}
}
then(onFulfilled, onRejected) {
onFulfilled(this.value);
onRejected(this.reason);
}
}
上面是我们根据基础要求写出的基本代码,它满足了Promise关于PENDING FULFILLED REJECTED三种状态,和一个then方法的特性。但是技术细节并不完善,下面我们一一实现这些技术细节。
状态凝固
const Promise = require('./promise');
const p = new Promise((resolve, reject) => {
resolve('成功');
reject('失败');
});
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
},
);
当我们使用我们自己的Promise的时候,执行上述代码会发现依次打印成功 失败这是不符合Promise标准的。
Promise拥有三种状态,但是只有在处于PENDING状态时,才可以进行更改,而且在更改之后状态就会凝固不能进行更改,这里需要我们对resolve、reject和then方法进行改造。
const resolve = (value) => {
+ if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
+ }
};
const reject = (reason) => {
+ if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
+ }
};
then(onFulfilled, onRejected) {
+ if (this.status === FULFILLED) {
onFulfilled(this.value);
+ }
+ if (this.status === REJECTED) {
onRejected(this.reason);
+ }
}
异步操作
const Promise = require('./promise');
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 0);
});
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
},
);
当我们引入异步操作的时候,发现这里并不能打印出成功这显然是不符合预期的,这里也是因为我们调用then方法时,状态并没有立即改变,所以没有执行对应的操作,这里我们对then方法进行一下改造,使用发布-订阅的方式,将存储的方法统一存储,然后在改变状态的时候,统一处理这些方法。
constructor(executor) {
+ this.onResolvedCallbacks = []; // 存储成功回调
+ this.onRejectedCallbacks = []; // 存储失败回调
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
+ this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
+ this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (e) {
// 如果执行时发生了异常 那么这个异常就是失败的原因
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
+ if (this.status === PENDING) {
+ this.onResolvedCallbacks.push(() => {
+ onFulfilled(this.value);
+ });
+ this.onRejectedCallbacks.push(() => {
+ onRejected(this.reason);
+ });
+ }
}
完整代码
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
this.status = PENDING; // 默认的pending状态
this.onResolvedCallbacks = []; // 存储成功回调
this.onRejectedCallbacks = []; // 存储失败回调
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (e) {
// 如果执行时发生了异常 那么这个异常就是失败的原因
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}