这篇文章通过自己实现一个Promise
来加深对promise的理解,具体如何实现符合promise
规范的代码,可以查看Promises/A+
梳理 Promise 功能
1、Promise
是一个构造函数,它接收一个执行函数作为参数,执行函数里面包含resolve
和reject
两个方法;
2、调用resolve
方法表示成功,reject
方法表示失败,结果会在实例的then
函数中以参数函数的方式拿到;
3、then
是实例方法,参数包含成功的回调函数和失败的回调函数,只用resolve
或reject
执行后方法才会执行;
状态变化
promise
里面的状态决定了当前能执行什么方法,先定义三个变量来保存状态:
const PENDING_STATUS = "pending"; // 等待,用户还没调用resolve或者reject
const FULFILLED_STATUS = "fulfilled"; // 成功,用户调用了resolve
const REJECTED_STATUS = "rejected"; // 失败,用户调用了reject
有状态就会有出现状态的原因了,后面还需要把这些原因传给then
方法,我们先创建构造函数:
function Promise(executor) {
this.status = PENDING_STATUS;
this.value = null; // 保存resolve方法的参数
this.reason = null;// 保存reject方法的参数
}
定义好了状态我们还要知道什么时候去修改它,我们知道executor
执行函数接受resolve
和reject
两个方法,这两个方法执行的时候就是我们修改状态的时候了:
function Promise(executor) {
this.status = PENDING_STATUS;
this.value = null; // 保存resolve方法的参数
this.reason = null;// 保存reject方法的参数
const resolve = (value)=>{
// 如果状态不是 pending 返回
if (this.status !== PENDING_STATUS) return;
// 先保存参数
this.value = value;
// 改变状态
this.status = FULFILLED_STATUS;
}
const reject = (reason)=>{
if (this.status !== PENDING_STATUS) return;
this.reason = reason;
this.status = REJECTED_STATUS;
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then 方法
我们知道then
方法接受两个参数,成功后的函数回调和失败后的函数回调,那么我们可以在函数里面判断当前状态,并执行对应的方法:
Promise.prototype.then = function (onfulfilled, onrejected){
if (this.status === FULFILLED_STATUS){}
if (this.status === REJECTED_STATUS){}
if (this.status === PENDING_STATUS){}
}
根据状态去判断执行onfulfilled
还是onrejected
方法比较容易,但是如果状态仍处于 pending
,就不能直接执行onfulfilled
或onrejected
了,我们可以利用发布订阅的方式将方法缓存起来,直到resolve
或reject
被调用才取出来执行,我们先在构造函数里面定义onResolvedCallback
和onRejectedCallback
两个数组:
function Promise(executor) {
...
this.onResolvedCallback = [];
this.onRejectedCallback = [];
...
}
then
方法里面,我们在pending
状态时先将执行函数保存起来:
Promise.prototype.then = function (onfulfilled, onrejected){
if (this.status === FULFILLED_STATUS){
onfulfilled(this.value);
}
if (this.status === REJECTED_STATUS){
onrejected(this.reason);
}
if (this.status === PENDING_STATUS){
this.onResolvedCallback.push(()=>{
onfulfilled(this.value);
})
this.onRejectedCallback.push(()=>{
onrejected(this.reason);
})
}
}
在resolve
和reject
函数中取出并执行:
const resolve = (value)=>{
if (this.status !== PENDING_STATUS) return;
this.value = value;
this.status = FULFILLED_STATUS;
// 取出缓存的方法并执行
this.onResolvedCallback.forEach(fn=>fn());
}
链式调用
promise
方法可以实现链式调用,上一个函数onfulfilled
的返回结果,可以在下一次then
函数中被获取:
new Promise((resolve)=>resolve("result1"))
.then(r=>`result2`)
.then(r=>`result3`)
.then(r=>`result4`);
可以看到then
方法可以不断的被链式调用,这说明then
方法也返回了一个promise
实例:
Promise.prototype.then = function (onfulfilled, onrejected){
let promise = new Promise((resolve,reject)=>{
if (this.status === FULFILLED_STATUS) {
onfulfilled(this.value);
}
if (this.status === REJECTED_STATUS) {
onrejected(this.reason);
}
if (this.status === PENDING_STATUS) {
this.onResolvedCallback.push(() => {
onfulfilled(this.value);
})
this.onRejectedCallback.push(() => {
onrejected(this.reason);
})
}
});
return promise;
}
但是onfulfilled
和onrejected
的返回结果,有可能是普通的值或者是promise
,所以我们也要处理下返回的值:
let promise = new Promise((resolve,reject)=>{
if(this.status === FULFILLED_STATUS){
let value = onfulfilled(this.value);
// 函数在setTimeout里面执行,这样能保证拿到返回的promise变量
setTimeout(()=>{
try {
resolvePromise(promise,value,resolve,reject);
} catch (error) {
onrejected(error)
}
})
}
if(this.status === REJECTED_STATUS){
let value = onrejected(this.reason);
setTimeout(() => resolvePromise(promise, value, resolve, reject));
}
if(this.status === PENDING_STATUS){
this.onResolvedCallback.push(()=>{
let value = onfulfilled(this.value);
setTimeout(() => {
try {
resolvePromise(promise, value, resolve, reject);
} catch (error) {
onrejected(error)
}
})
});
this.onRejectedCallback.push(()=>{
let value = onrejected(this.reason);
setTimeout(() => resolvePromise(promise, value, resolve, reject));
})
}
});
resolvePromise
函数我们要做的处理是判断返回的value
值,这里有几个判断需要注意:
- value 跟返回的 promise 相同,循环引用直接抛出错误
- value 是
Promise
实例,在then
方法里面执行resolve
- 直接
resolve
value
function resolvePromise(promise,value,resolve,reject){
if (promise === value) throw new Error("循环引用");
if(value instanceof Promise){
let then = value.then;
try {
then.call(value,(result)=>{
resolve(result);
},(error)=>{
reject(error);
})
} catch (error) {
reject(error)
}
}else{
resolve(value);
}
}
值的穿透
Promise
支持一种比较奇葩的写法:
let p = new Promise((resolve)=>resolve("result"));
p.then().then().then(result=>console.log(result));
遇到这种写法,需要在then
函数里面提前做判断:
Promise.prototype.then = function(onfulfilled,onrejected){
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : (val)=>val;
onrejected = typeof onrejected === "function" ? onrejected : (error)=>error;
...
}
catch
Promise.prototype.catch = function (errorCallback) {
return this.then(null, errorCallback)
}
resolve 和 reject
Promise.resolve = function (value) {
return new Promise((resolve) => resolve(value))
}
Promise.reject = function (value) {
return new Promise((resolve, reject) => reject(value))
}
Promise.all
all
方法接受一个数组,我们需要判断数组里面的Promise
方法,只有在then
方法执行后才算完成,可以定义一个完成变量保存完成的数量,当完成数量等于数组长度时,才resolve
最终的结果
Promise.all = function (values) {
return new Promise((resolve, reject) => {
let result = [];
let resultIndex = 0; // 执行完成的数量
let len = values.length;
function processData(index, value) {
result[index] = value;
resultIndex++; // 执行完成后加一
if (resultIndex === len) {
// 最终结果
resolve(result);
}
}
for (let i = 0; i < len; i++) {
let value = values[i];
let then = value.then;
if (then && (typeof then === "function")) {
then.call(value, result => {
processData(i, result)
}, reject => {
processData(i, reject)
})
} else {
processData(i, value)
}
}
})
}
race
race
执行返回最快完成的结果,所以当then
函数执行后直接执行resolve
返回结果:
Promise.race = function (values) {
return new Promise((resolve, reject) => {
for (let i = 0; i < values.length; i++) {
let value = values[i];
let then = value.then;
if (then && (typeof then === "function")) {
then.call(value, result => {
resolve(result)
}, reject)
} else {
resolve(value);
break;
}
}
})
}