手写Promise对象及代码详解

手撕Promise代码带你无敌带你飞(bushi


最近开始研究理♂论知识,博主很是头疼啊。用了一下午时间算是搞懂了Promise手写代码的一些内♂容(应该),有任何不对的地方和想法建议都欢迎评论,也可以和博主私聊啊啊啊啊我真的太菜了

什么是Promise对象

这玩意一百度太多了,重要的点就是

  1. Promise的状态一经改变就变成了不可选取的状态,不会再发生改变,
pending => fulfilled
//or
peding => rejected
  1. (异步)操作成功时,会执行回调函数 resolve , 失败则会执行 rejected
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value); //成功
  } else {
    reject(error); //失败
  } 
});
  1. then应该位于Promise.prototype上,因为每一个Promise实例都可以使用then方法,而且then方法也有两个函数参数,对应着 成功/失败 时候的回调函数
Promise.prototype.then(onFulfilled,onRejected)
  1. Promise对象创建的时候,接收的是一个函数,而这个函数的两个参数resolverejected,只不过这两个参数也是两个函数,也就是我们说的回调函数

手撕Promise代码

  1. 我们可以根据以上三点先得到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`抛出异常即可。
  1. 当然Promise的状态我们还没有加进去,而且resolvereject是可以带参数的,并且这个参数是可以保存的,这个也要写进去。以及两个回调函数也要写进构造函数供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`的时候会改变状态
  1. 接下来是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`方法
  1. 感觉写到这里就完成了?NO!,我们现在还并没有适配异步的情况,请看下图:
    Promise代码流程执行图
    感谢 知乎@小飞 的 图(😀)
  2. 所以我们可以看出,当存在异步的运行情况时,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方法。因此我们从以下几个角度来考虑

  • 如果onFulfilledonRejected返回了一个值x,那我们应该像处理Promise一样处理这个x,这里的处理我们用一个函数封装
    resolvePromise(promise, x, resolve, reject) 至于为什么传一个promise,后面解释
  • 如果onFulfilledonRejected抛出一个异常,那我们应该使用reject去处理这个异常
    reject(e)
  • 如果onFulfilledonRejected本身就不是函数而是一个常量(?),那么就应当把这个值继续传递下去。这里是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结束或者返回值是函数
到这里为止,我们只是对onFulfilledonRejected的返回值做了一个储存。接下来我们就要使用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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值