手写实现一个promise

117 阅读5分钟

1.promise接收一个函数做为参数

2.promise中有then方法,then方法中可以拿到promise的执行结果

3.promise可以链式调用

class myPromise {
  constructor(fn) {
      // 在此处执行fn
      fn(resolve)
  }
// 因为then方法可以实现链式调用,所以then方法返回的还是promise
  then() {return this}
}


// 我们来new 一个promise, 我们可以知道promsie在第一秒的时候resolve一个值出去
// 也就是说then方法在promise实例生成一秒后才会有返回结果

let promsie = new myPromse(resolve => {
    setTimeout(() => {
        resolve(5)
},1000)
})

promise.then(res => {
   // 在实例生成一秒后,才有结果返回
   console.log(res)
})

/* 我们发现在生成实例时,执行的fn就是我们传进去的函数,而这个函数接收一个resolve作为参数,
不难发现我们传进去的resolve是一个函数,因为resolve在setTimeout中被执行了,那我们就想了,
这个resolve的作用是什么呢?作用把异步函数的返回值拿到,并传到then方法中
这个resolve到底是在哪定义的函数呢? resolve定义在构造函数里比较合适
这个resolve又应该怎么写呢?

我们可以知道的是,在实例生成后一秒,实例resolve出去一个值,这个值就是then放发执行res的值,
那么resolve方法肯定和当前实例的then方法有联系,他们俩怎么样才能联系起来呢?我陷入了深思
..........................

*/

经过上面稀里糊涂的分析,我们的promise长成了这样:

class myPromise {

  constructor(fn) {
     
      // 由于resolve是一个实例方法所以我们要把fn中的resolve绑定成当前promise实例
      // 不然resolve方法执行的时候this指向会发生错误

      fn(this.resolve.bind(this))
  }
// 因为then方法可以实现链式调用,所以then方法返回的还是promise
  then() {return this}
  //
  resolve(value) {

  }
}

/*因为resolve方法是在实例生成的第一秒执行的,所以在实例生成的0-1秒then方法是拿不到实列的结果的,所以
then方法里肯定做了些神奇的操作,
那我们就想了then方法到底做了哪些操作呢?
我们可以知道的是then方法接收的也是一个方法作为参数,那我们就想到了,我们等resolve执行后再执行then方法
里的函数不就能拿到结果了吗?
那我们应该怎么操作呢?
看过相关promise实现的同学都知道,当我们执行then方法但是拿不到结果的时候,我们就会把then方法中的这个方法
push到一个数组中,
再到resolve执行的时候再拿到这个数组,并依次执行数组中的函数
*/

所以我们的promise现在应该长这个样子

class myPromise {
  value = ''; 用于存放promise的reslove值
  callbacks = [] ; // 用于存放then中的方法
  state = 'padding'; // 用于判断当前promise是否拿到结果
  constructor(fn) {
     
      // 由于resolve是一个实例方法所以我们要把fn中的resolve绑定成当前promise实例,不然我们会找不到
      // resolve方法执行的时候会找不到当前对象是谁

      fn(this.resolve.bind(this))
  }
// 因为then方法可以实现链式调用,所以then方法返回的还是promise
  then(callback) {
      if(this.state === 'padding') {
         this.callbacks.push(callback)
      } else {
        //直接执行res中的方法把promise实例resolve的值带出去
         callback(this.value)
      }
    return this
  }
  //
  resolve(value) {
       this.state = 'fullfiled'; // resolve执行的时候改变promise的状态
       this.value = value; // 把resolve的值缓存起来,下次调用then方法时直接使用
       // 查看当前实例的callbacks中是否有待执行的函数,有的话执行,并带入resolve的值
       this.callbacks.forEach(callback => {
           callback(this.value)
       })
  }
}

但是我们不难发现,then方法中返回的this用永远是我们创建的这个实例,这样就导致链式调用的时候,我们拿到的值永远都是我们创建这个实例的resolve值,这不是我们想要的结果,我们想要的结果是then方法处理后返回的结果,我们需要进行下面的处理

class myPromise {
	state = 'padding';
	callbacks = [];
	constructor(fn) {
		fn(this.resolve.bind(this))
		}
	then(fn) {
		return new myPromise(resolve => {
			this._hander({
			    resolve: resolve,
			    fn: fn
			})
		})
	}
	_hander(callback) {
		if(this.state === 'padding') {
			this.callbacks.push(callback)
		} else if (typeof callback.fn === 'function'){
		   let ret = callback.fn(this.value)
		   callback.resolve(ret)
		} else {
			callback.resolve(this.value)
		}
	}
	resolve(value) {
		this.state = 'fullfiled';
		this.value = value;
		this.callbacks.forEach( callback => callback.fn(value))
	}
}

由上面的代码可知,我们在promise的构造函数中又加了一_handler个实例方法,我们在执行实例的then方法的时候return出去一个新的promise,并且在这个promise中传入当前then方法的回调函数fn和promise需要的形参resolve。

新生成的promise会执行上一个实例的handler的方法,在handler方法中我们判断上一个实例的state的状态,如果是padding我们就往上一个实例中push进去当前的callback,否则我们执行callback中发fn,并把fn的返回值当作新生成的promise值resolve出去,这样我们的链式调用才有了一定的意义。

但是我们发现如果then方法的返回值不是一个value的类型而是一个新的promise时,我们手写的promsie又出现问题了

那我们下面就来解决then方法返回值时一个promise的情况

class myPromise {
	state = 'padding';
	callbacks = [];
	constructor(fn) {
		fn(this.resolve.bind(this))
		}
	then(fn) {
		return new myPromise(resolve => {
			this._hander({
			    resolve: resolve,
			    fn: fn
			})
		})
	}
	_hander(callback) {
		if(this.state === 'padding') {
			this.callbacks.push(callback)
		} else if (typeof callback.fn === 'function'){
		   let ret = callback.fn(this.value)
		   callback.resolve(ret)
		} else {
			callback.resolve(this.value)
		}
	}
	resolve(value) {
		if(typeof value === 'object'&& typeof value.then === 'function') {
			value.then(this.resolve.bind(this))
			return;
		}
		this.state = 'fullfiled';
		this.value = value;
		this.callbacks.forEach( callback => callback.fn(value))
	}
}

我们在resolve函数中加一个判断判断当前的value值是不是一个promise如果是的话,我们执行这个promise的then方法,并且绑定当前实例的reslove。这个resolve的作用很重要,他是为了把ret的值转换成一个value返回出去。这样如果then方法的返回值是一个promise的话,我们在链式调用的时候就能拿到这个promise的返回值了。