手撕Promise代码带你无敌带你飞(bushi
最近开始研究理♂论知识,博主很是头疼啊。用了一下午时间算是搞懂了Promise手写代码的一些内♂容(应该),有任何不对的地方和想法建议都欢迎评论,也可以和博主私聊啊啊啊啊我真的太菜了
什么是Promise对象
这玩意一百度太多了,重要的点就是
- Promise的状态一经改变就变成了不可选取的状态,不会再发生改变,
pending => fulfilled
//or
peding => rejected
- (异步)操作成功时,会执行回调函数
resolve
, 失败则会执行rejected
。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value); //成功
} else {
reject(error); //失败
}
});
then
应该位于Promise.prototype
上,因为每一个Promise实例都可以使用then方法,而且then方法也有两个函数参数,对应着 成功/失败 时候的回调函数
Promise.prototype.then(onFulfilled,onRejected)
- Promise对象创建的时候,接收的是一个函数,而这个函数的两个参数是
resolve
和rejected
,只不过这两个参数也是两个函数,也就是我们说的回调函数
手撕Promise代码
- 我们可以根据以上三点先得到Promise这个构造函数的大体,暂时先不写处理逻辑
//首先创建三种状态的常量,这样的写法是可以找到报错原因的(叹气.gif)
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected;
//开始书写 构造函数
function myPromise(fn) {
try {
fn(resolve,reject);
}catch(e) {
reject(e)
}
}
到这里为止应该很好懂。`myPromise`接收的参数是一个函数,Promise会自动执行这个函数,函数的参数是两个回调函数,同时如果产生异常那么直接通过`reject`抛出异常即可。
- 当然Promise的状态我们还没有加进去,而且
resolve
和reject
是可以带参数的,并且这个参数是可以保存的,这个也要写进去。以及两个回调函数也要写进构造函数供fn
调用
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected;
//开始书写 构造函数
function myPromise(fn) {
const that = this; //保存的是myPromise这个构造函数的this,防止闭包(口区.gif)
that.value = null;
that.status = PENDING;
function resolve(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = FULFILLED;
}
}
function reject(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = REJECTED;
}
}
try {
fn(resolve,reject);
}catch(e) {
reject(e)
}
}
这里应该不难懂,两个回调函数是可以接收参数的并且可以保存,同时执行`rejected`和`resolve`的时候会改变状态
- 接下来是
then
方法。因为then方法是可以调用回调函数的。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected;
//开始书写 构造函数
function myPromise(fn) {
const that = this; //保存的是myPromise这个构造函数的this,防止闭包(口区.gif)
that.value = null;
that.status = PENDING;
function resolve(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = FULFILLED;
}
}
function reject(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = REJECTED;
}
}
try {
fn(resolve,reject);
}catch(e) {
reject(e)
}
myPromise.prototype.then = function(onFulfilled,onRejected) { // 这里两个参数就是 成功/失败 对应的回调函数,取名字不同而已,莫慌
let self = this; //self是myPromise这个构造函数的this
if(self.status === FULFILLED) {
onFulfilled(self.value) // 可以调用参数,默认是之前状态改变时保存的数据
}
if(self.status === REJECTED) {
onRejected(self.value)
}
}
}
写到这里就是完成了`then`方法的书写,记住一定也写在Promise的原型上,因为所有Promise实例都可以使用`then`方法
- 感觉写到这里就完成了?NO!,我们现在还并没有适配异步的情况,请看下图:
感谢 知乎@小飞 的 图(😀) - 所以我们可以看出,当存在异步的运行情况时,
pending
还没有被确定,没有办法执行回调函数。我们可以参考发布订阅模式【个人感觉这篇博客很棒】,在执行then
方法的时候,如果没有确定pending,先把回调函数存入一个数组,等状态确定后,再去执行数组中的所有回调函数。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected;
//开始书写 构造函数
function myPromise(fn) {
const that = this; //保存的是myPromise这个构造函数的this,防止闭包(口区.gif)
that.fulfilledCallback = []; //成功状态回调函数
that.rejectedCallback = []; //失败状态回调函数
that.value = null;
that.status = PENDING;
function resolve(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = FULFILLED;
that.fulfilledCallback .forEach(fulFn => fulFn(that.value)) //执行所有回调函数
}
}
function reject(value) { //这里的value是我们传进去的参数,可以保存在Promise内部
if(that.status === PENDING) {
that.value = value;
that.status = REJECTED;
that.rejectedCallback.forEach(rejFn => rejFn(that.value)) //执行所有回调函数
}
}
try {
fn(resolve,reject);
}catch(e) {
reject(e)
}
myPromise.prototype.then = function(onFulfilled,onRejected) { // 这里两个参数就是 成功/失败 对应的回调函数,取名字不同而已,莫慌
let self = this; //self是myPromise这个构造函数的this
if(self.status === FULFILLED) {
onFulfilled(self.value) // 可以调用参数,默认是之前状态改变时保存的数据
}
if(self.status === REJECTED) {
onRejected(self.value)
}
//状态仍是pending的时候,把回调函数压入数组中
if(self.status === PENDING) {
self.fulfilledCallback.push(onFulfilled);
self.rejectedCallback.push(onRejected);
}
}
}
至此,一个超级无敌简易的Promise就写好了,目前只是实现了then
方法,晚上有个本不属于我的会(大哭.gif),下次找个时间写一写all
方法把,溜了溜了~
给一个DEMO
let promise = new myPromise((resolve,reject) => {
console.log('first');
setTimeout(() => {
resolve(5);
},1000)
});
promise.then((res) => {
console.log(res);
})
输出
点赞的每天都好起来
-------------------------------------------------------------------更新---------------------------------------------------------------
Promise.then 链式
对于链式then,这是Promise最神奇的地方。then
的返回值是一个新的Promise对象,所以对于then
的链式写法, 我们首先考虑,第一次使用then
返回的到底是不是Promise对象,或者说,到底具不具备then
方法。因此我们从以下几个角度来考虑
- 如果
onFulfilled
或onRejected
返回了一个值x,那我们应该像处理Promise一样处理这个x,这里的处理我们用一个函数封装
resolvePromise(promise, x, resolve, reject)
至于为什么传一个promise,后面解释 - 如果
onFulfilled
或onRejected
抛出一个异常,那我们应该使用reject
去处理这个异常
reject(e)
- 如果
onFulfilled
或onRejected
本身就不是函数而是一个常量(?),那么就应当把这个值继续传递下去。这里是resolve,那么promise2也应该resolve,这里是reject,那么promise也应该是reject
所以我们把myPromise.prototype.then
方法稍微改写以下
myPromise.prototype.then = function(onFulfilled,onRejected) {
let self = this;//保持与构造函数同一个对象
//首先判断回调函数到底是不是函数
onFulfilled = typeof onFulfilled==='function' ? onFulfilled : value=> value;
onRejected= typeof onRejected==='function' ? onRejected: value=> value;
//使用setTimeout是为了保证then是异步的
if(self.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(self.value); //x就是那个返回值,Promise对象或一个常量
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(self.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(self.value); //x就是那个返回值,Promise对象或一个常量
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(self.status === PENDING) {
self.fulfilledCallback.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
self.rejectedCallback.push(() => {
setTimeout(() => {
try {
let x = onRejected(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
}
要点就是:只要返回值不是函数,就一直传下去直达then
结束或者返回值是函数
到这里为止,我们只是对onFulfilled
和onRejected
的返回值做了一个储存。接下来我们就要使用resolvePromise
去处理这个返回值,这里又有以下的情况
- 返回值和promise2相等。
这就是一种错误了,相当于第一个then
得到的promise,就是第二个then
里面给的回调函数的返回值,这明显是不可能的 - 返回值不是promise,那么就以x为参数执行promise
- 返回值是promise
- 就像处理一般的promise一样处理了。
- 返回值是函数or对象
- 把
x.then
赋值给then
,出错了报错 then
是函数的话继续如上操作。
最后的代码也就是下面的啦~
- 把
function reslovePromise(promise2, x, reslove, reject) {
if(promise2 === x) {
reject(new TypeError('Chaining cycle'));
}
if(x !== null && (typeof x==='object' || typeof x==='function')) {
try {
let then = x.then;
then.call(x,(res) => {
reslove(res);
},(err) => {
reject(err);
})
} catch(e) {
reject(e);
}
}
else {
reslove(x);
}
}
myPromise.prototype.then = function (onFulfilled,onRejected) {
let self = this; //self是myPromise这个构造函数的this
onFulfilled = typeof onFulfilled==='function' ? onFulfilled : value=> value;
onRejected = typeof onRejected==='function' ? onRejected : value=> value;
var promise2 = new myPromise((resolve,reject) => {
if(self.status === FULFILLED) {
// 可以用于链式的情况 是resolve/reject有返回值 Promise对象or常量,则x就是那个Promise对象or常量
setTimeout(() => {
try {
let x = onFulfilled(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(self.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
}
if(self.status === PENDING) {
self.fulfilledCallback.push(() => {
console.log('one');
setTimeout(() => {
try {
let x = onFulfilled(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
});
self.rejectedCallback.push(() => {
setTimeout(() => {
try {
let x = onRejected(self.value);
reslovePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
})
return promise2;
}