Promise 的基本原理
- Promise 是一个类,在执行这个类的时候会传入一个函数,这个函数会立即执行
- Promise 有三种状态
- Pending 等待
- Fulfilled 成功
- Rejected 失败
- 状态的改变只能有两种
- 从 Pending 到 Fulfilled
- 从 Pending 到 Rejected
- 上面状态改变只能二选一,状态一旦发生改变就不会再修改
- Promise 中 resolve 和 reject 两个函数用来更改状态
- then 通过状态执行函数
- 如果状态是成功,执行成功回调函数
- 如果状态是失败,执行失败回调函数
先确定原生 promise 的实现功能,要了解原生代码 🌰 如下:
const promise = new Promise((resolve, reject) => {
resolve('yes');
reject('on');
})
promise.then(value => {
console.log('resolve:', value);
}, error => {
console.log('reject:', error);
})
// 输出 resolve: yes
复制代码
Promise 的实现
1. 新建 MyPromise 类,传入函数 executor,executor 传入 resolve 和 reject 方法
// 新建 MyPromise 类
class MyPromise {
constructor(executor) {
executor(); // executor 进入会立即执行
executor(this.resolve, this.reject); // 并传入resolve和reject方法
}
// 用箭头函数 this 指向当前实例对象
resolve =()=> {}
reject =()=> {}
}
复制代码
2. 增加状态管理
// 三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 新建 MyPromise 类
class MyPromise {
constructor(executor) {
this.status = PENDING; // 储存状态
this.value = null; // 成功之后的值
this.reason = null; // 失败之后的值
executor(this.resolve, this.reject); // 立即执行并传入resolve和reject方法
}
// 用箭头函数 this 指向当前实例对象
resolve =(value)=> {
if(this.status === PENDING) { // 只有状态是等待,才执行状态修改
this.status = FULFILLED;
this.value = value;
}
}
reject =(reason)=> {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
}
复制代码
3. then 方法实现
then 方法接受两个参数 onFulfilled、onRejected,它们分别在状态由 PENDING 改变为FULFILLED、REJECTED后调用
MyPromise.prototype.then = function(onFulfilled, onRejected)
if (this.status === FULFILLED) {
onFulfilled(this.value); // 调用成功回调,并且把值返回
} else if (this.status === REJECTED) {
onRejected(this.reason); // 调用失败回调,并且把原因返回
}
}
复制代码
4. 执行代码,验证结果
const promise = new MyPromise((resolve, reject) => {
resolve('yes')
reject('no')
})
promise.then(value => {
console.log('resolve', value);
}, reason => {
console.log('reject', reason);
})
// resolve yes
复制代码
执行结果符合预期,此处应该开心一下 😁
5. 加入异步逻辑
引入 setTimeout, setTimeout 会加入到异步队列,then 会在 setTimeout 之前执行,then 方法中的状态为 pending,没有对 pending 状态做处理,因此没有输出。
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('yes')
}, 2000);
})
promise.then(value => {
console.log('resolve', value);
}, reason => {
console.log('reject', reason);
})
// 没有任何输出
复制代码
6. 处理 pending 逻辑以及 then 链式调用
class MyPromise{
constructor(executor) {
...
this.onFulfilledCallbacks = []; // 存储成功回调函数
this.onRejectedCallbacks = []; // 存储失败回调函数
...
}
resolve =(value)=> {
if(this.status === PENDING) {
...
this.onFulfilledCallbacks && this.onFulfilledCallbacks.forEach((onFulfilledCallback) => {
onFulfilledCallback(value);
});
}
}
reject =(reason)=> {
if(this.status === PENDING) {
...
this.onRejectedCallbacks && this.onRejectedCallbacks((onRejectedCallback) => {
onRejectedCallback(reason);
});
}
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected)
if (this.status === FULFILLED) {
...
} else if (this.status === REJECTED) {
...
} else if(this.status === PENDING) {
this.onFulfilledCallback = onFulfilled;
this.onRejectedCallback = onRejected;
}
}
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('yes')
}, 2000);
})
promise.then(value => {
console.log(1)
console.log('resolve', value);
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
// 1
// resolve yes
// 2
// resolve yes
复制代码
7. 优化 then 异步 以及 链式调用
虽然 resolve 是同步执行的,必须保证 then 是异步调用的,用 setTimeout 来模拟异步调用。 保证链式调用,即 then 方法中要返回一个新的 promise,并将 then 方法的返回值进行 resolve。
MyPromise.prototype.then = function(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value); // 调用成功回调,并且把值返回
resolve(x);
} catch (reason) {
reject(reason);
}
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason); // 调用失败回调,并且把原因返回
resolve(x);
} catch (reason) {
reject(reason);
}
}, 0);
} else if(this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value); // 调用成功回调,并且把值返回
resolve(x);
} catch (reason) {
reject(reason);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason); // 调用失败回调,并且把原因返回
resolve(x);
} catch (reason) {
reject(reason);
}
}, 0);
});
}
})
}
new MyPromise((resolve, reject) => {
resolve('yes1');
console.log(1);
}).then(value => {
console.log(2);
}).then(value => {
console.log(3);
});
// 1 2 3
复制代码
上面 promise 、then 方法链式调用已基本实现,开心一下吧 😁
8. 实现 all 方法
Promise.all 方法可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。all 方法接受一个 promise 数组,当所有 promise 状态 resolve 后,执行 resolve。
MyPromise.prototype.all = function(promises) {
return new MyPromise((resolve, reject) => {
if(promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for(let i=0; i<result.length; i++) {
promises[i].then(value => {
result[i] = value;
index ++;
if(index === promises.length) {
resolve(result);
}
}, err => {
reject(err);
return
});
}
}
});
};
复制代码
9. 实现 race 方法
race 是赛跑的意思,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。接受一个 promise 数组,当有一个 promise 状态 resolve 后,执行 resolve。
MyPromise.prototype.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve();
} else {
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => {
resolve(data);
}, err => {
reject(err);
return;
});
}
}
});
}
复制代码