手写Promise

Promise应该长什么样

let p = new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve(100)
		}, 1000)
	}
)

console.log(p)

在控制台上可以看到
在这里插入图片描述

PromiseState就是Promise对象的状态
PromiseResult是state为fulfilled时resolve的value 或 为rejected时reject的reason

看一下Promise对象原型
在这里插入图片描述

可以看到原型上有这么三个方法

  • then
  • catch
  • finally

再看一下Promise构造函数
在这里插入图片描述
可以看到构造函数身上有这么几个方法

  • all
  • allSettled
  • any
  • race
  • reject
  • resolve

所以,Promise是什么,长什么样,已经很清晰了。

Promise是一个class,原型上有then,catch,finnaly方法,类上有这么六个静态方法all,allSettled,any,race,reject,resolve

Promise初步形式实现

let p = new Promise((resolve, reject) => {
		setTimeout(() => {
			console.log(1)
			resolve(100)
		}, 1000)
	}
)

通过这段真正Promise构造函数的调用,我们知道了些什么

  • Promise的构造函数需要传入一个参数,参数类型为function,称之为executor
  • 该函数接收resolve,reject两个参数(类型都为function),client要在函数body中使用这两个函数实现状态的凝固,故这两个函数是Promise自己来实现的
  • 打开控制台的话,会发现打印了一个1,知new Promise的同时,会执行executor函数体中的内容
class MyPromise {
	constructor(executor) {
		const resolve = (value) => {
			// do something
		}
		const reject = (reason) => {
			// do something
		}
		// 执行executor函数
		executor(resolve, reject)
	}
}

这样就形式上实现了new Promise的操作

Promise中then方法的实现

let p = new Promise((resolve, reject) => {
		resolve(100)
	}
)
p.then(value => {
	console.log(value)
}, reason => {
	console.log(reason)
})

阅读这段代码,结合我们知道的Promise知识,得到以下要点

  • Promise对象有三个状态,pending,fulfilled,rejected
  • 调用resolve方法后,状态从pending变为fulfilled并凝固,保存传入的value
  • 调用reject方法后,状态从pending变为rejected并凝固,保存传入的reason
  • then方法有两个参数,onfulfilledonrejected,都是function,分别作为Promise状态为fulfilledrejected的回调。
  • onfulfiiled接收一个参数value,onrejected接收一个参数reason
class MyPromise {
	constructor(executor) {
		// 为Promise对象添加state属性,初始为'pending'
		this.state = 'pending'
		// 为Promise对象添加value属性,作为成功时的结果
		this.value = undefined
		// 为Promise对象添加reason属性,作为失败时的结果
		this.reason = undefined
		
		const resolve = (value) => {
			// 状态凝固
          	if (this.state === 'pending') {
            	this.state = 'fulfilled'
            	this.value = value
          	}
        }
        const reject = (reason) => {
        	//状态凝固
          	if (this.state === 'pending') {
            	this.state = 'rejected'
            	this.reason = reason
          	}
        }
		executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		if (this.state === 'fulfilled') {
			onfulfilled(this.value)
		}
		if (this.state === 'rejected') {
			onrejected(this.reason)
		}
	}
}

好了,至此为此,是一个粗略的实现,可以满足同步执行
now have a try!

let p = new MyPromise((resolve, reject) => {
		resolve(100)
	}
)
p.then(value => console.log(value))

控制台结果是100

但不支持异步,举个例子

let p = new MyPromise((resolve, reject) => {
		setTimeout(() => {
			resolve(100)
		}, 1000)
	}
)
p.then(value => console.log(value))

控制台什么结果也没有

为什么?非常简单,new Promise时启动了setTimeout,然后执行p.then方法,这时候p中还没有调用过resolve方法,即p.state仍为pending,在then方法的逻辑中,没有可供执行的代码,传入的onfulfilled函数并不会被调用

问题来了,怎么让它支持异步?

then中传入的回调函数,需要在状态改变时执行
对应地,就是Promise中定义的resolve和reject方法
但为了支持多次.then方法的调用,应当将回调函数放入一个数组中,而不是覆盖

class MyPromise {
	constructor(executor) {
		this.state = 'pending'
		this.value = undefined
		this.reason = undefined
		// 保存成功的回调函数
		this.onResolvedCallbacks = []
		// 保存失败的回调函数
		this.onRejectedCallbacks = []
		
		const resolve = (value) => {
			if (this.state === 'pending') {
				this.state = 'fulfilled'
				this.value = value
				// 按顺序执行所有的成功的回调函数
				this.onResolvedCallbacks.forEach(fn => fn())
			}
		}
		const reject = (reason) => {
			if (this.state === 'pending') {
				this.state = 'rejected'
				this.reason = reason
				// 按顺序执行所有的失败的回调函数
				this.onRejectedCallbacks.forEach(fn => fn())
			}
		}
		executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		if (this.state === 'fulfilled') {
			onfulfilled(this.value)
		}
		if (this.state === 'rejected') {
			onrejected(this.reason)
		}
		if (this.state === 'pending') {
			// 存储回调函数
			// 在执行时,自动传入执行时刻的this.value
			this.onResolvedCallbacks.push(() => onfulfilled(this.value))
			this.onRejectedCallbacks.push(() => onrejected(this.reason))
		}
	}
}

have a try

let p = new MyPromise((resolve, reject) => {
		setTimeout(() => {
			resolve(100)
		}, 1000)
	}
)
p.then(value => console.log(value, 1))
p.then(value => console.log(value, 2))

结果为100 1  ; 100 2

支持链式调用

想要支持链式调用,显而易见地,then方法需要返回一个Promise对象,初步编码如下

class MyPromise {
	constructor(executor) {
		this.state = 'pending'
		this.value = undefined
		this.reason = undefined
		this.onResolvedCallbacks = []
		this.onRejectedCallbacks = []
		
		const resolve = (value) => {
			if (this.state === 'pending') {
				this.state = 'fulfilled'
				this.value = value
				this.onResolvedCallbacks.forEach(fn => fn())
			}
		}
		const reject = (reason) => {
			if (this.state === 'pending') {
				this.state = 'rejected'
				this.reason = reason
				this.onRejectedCallbacks.forEach(fn => fn())
			}
		}
		executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		let p2 = new MyPromise((resolve, reject) => {
			if (this.state === 'fulfilled') {
				// 得到成功回调的返回值,并传给p2,使p2 fulfilled
				let x = onfulfilled(this.value)
				resolve(x)
			}
			if (this.state === 'rejected') {
				let x = onrejected(this.reason)
				resolve(x)
			}
			if (this.state === 'pending') {
				this.onResolvedCallbacks.push(() => {
					// 处理异步时同理
					let x = onfulfilled(this.value)
					resolve(x)
				})
				this.onRejectedCallbacks.push(() => {
					let x = onrejected(this.reason)
					resolve(x)
				})
			}
		})
		return p2
	}
}

have a try

let p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve(100)
      }, 1000)
    }
)
p.then(value => {
	console.log(value)
	return 200
}).then(value => console.log(value))

结果为100 200

但我们知道,onfulfilled方法的返回值会被包装上Promise返回出then,但如果onfulfilled中返回的是Promise对象,就不会再封装,对多层嵌套亦然。
在原本的Promise中,应该有

let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(100)
      }, 1000)
    }
)
p.then(value => {
	console.log(value)
	return new Promise((resolve, reject) => {
		resolve(200)
	})
}).then(value => console.log(value))

结果是100 200,但使用刚才封装的MyPromise显然不是,p.then的返回值是一个嵌套两层的Promise

其实在原本Promise对象中,如果发生Promise嵌套,都会被解析只剩下一层
譬如

Promise.resolve(Promise.resolve(Promise.resolve(100))).then(console.log)
控制台会直接输出100

故不应该对返回值x作判断,不妨在resolve和reject方法上下文章。修改如下

class MyPromise {
	constructor(executor) {
		this.state = 'pending'
		this.value = undefined
		this.reason = undefined
		this.onResolvedCallbacks = []
		this.onRejectedCallbacks = []
		
		const resolve = (value) => {
			// 定义箭头函数
			const handleResolve = x => {
				if (x instanceof MyPromise) {
					// 将外层resolve的值传入handleResolve递归
					x.then(handleResolve)
				} else {
					this.state = 'fulfilled'
					this.value = x
					this.onResolvedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleResolve(value)
			}
		}
		const reject = (reason) => {
			const handleReject = x => {
				if (x instanceof MyPromise) {
					x.then(handleReject)
				} else {
					this.state = 'rejected'
					this.reason = x
					this.onRejectedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleReject(reason)
			}
		}
		executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		let p2 = new MyPromise((resolve, reject) => {
			if (this.state === 'fulfilled') {
				let x = onfulfilled(this.value)
				resolve(x)
			}
			if (this.state === 'rejected') {
				let x = onrejected(this.reason)
				resolve(x)
			}
			if (this.state === 'pending') {
				this.onResolvedCallbacks.push(() => {
					let x = onfulfilled(this.value)
					resolve(x)
				})
				this.onRejectedCallbacks.push(() => {
					let x = onrejected(this.value)
					resolve(x)
				})
			}
		})
		return p2
	}
}

基本已经ok了,下面是处理一些特殊情况
在真实Promise中,面对这种情况

let p = new Promise((resolve, reject) => {
  resolve(1)
})

let p2 = p.then((res) => {
  console.log(res)
  return p2
})

控制台显示
在这里插入图片描述
在我们的MyPromise中,其实这种情况还好,MyPromise里传的是一个同步函数,这样执行p.then时,其中的回调也是立即执行,此时p2还没初始化,报错信息是这样的
在这里插入图片描述
或者你这么写

let p2
p2 = p.then(res => {
	console.log(res)
	return p2
})
console.log(p2)

这样甚至不会报错,你可以打印出p2,状态是fulfilled,value是undefined罢了

但当,MyPromise中传入的是一个异步函数就会出问题了

let p = new MyPromise((resolve, reject) => {
	setTimeout(() => {
		resolve(1)
	}, 1000)
})

let p2 = p.then((res) => {
  console.log(res)
  return p2
})

此时因为传入的是异步函数,onfulfilled回调并不会立即执行,那么p2就会被初始化了,就会出现问题
所以我们需要对这种情况进行捕获和抛出异常,模拟真实Promise

class MyPromise {
	constructor(executor) {
		this.state = 'pending'
		this.value = undefined
		this.reason = undefined
		this.onResolvedCallbacks = []
		this.onRejectedCallbacks = []
		
		const resolve = (value) => {
			const handleResolve = x => {
				if (x instanceof MyPromise) {
					x.then(handleResolve)
				} else {
					this.state = 'fulfilled'
					this.value = x
					this.onResolvedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleResolve(value)
			}
		}
		const reject = (reason) => {
			const handleReject = x => {
				if (x instanceof MyPromise) {
					x.then(handleReject)
				} else {
					this.state = 'rejected'
					this.reason = x
					this.onRejectedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleReject(reason)
			}
		}
		executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		let p2 = new MyPromise((resolve, reject) => {
			if (this.state === 'fulfilled') {
				let x = onfulfilled(this.value)
				//  设置定时器,保证p2被初始化再执行
				setTimeout(() => {
					if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
					else resolve(x)
				}, 0)
			}
			if (this.state === 'rejected') {
				let x = onrejected(this.reason)
				setTimeout(() => {
					if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
					else resolve(x)
				}, 0)
			}
			if (this.state === 'pending') {
				this.onResolvedCallbacks.push(() => {
					let x = onfulfilled(this.value)
					setTimeout(() => {
						if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
						else resolve(x)
					}, 0)
				})
				this.onRejectedCallbacks.push(() => {
					let x = onrejected(this.value)
					setTimeout(() => {
						if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
						else resolve(x)
					}, 0)
				})
			}
		})
		return p2
	}
}

好,最后再加一点小细节

  • new Promise时,传入的参数不是函数,Promise会报错
let p = new Promise(1)

在这里插入图片描述

  • onrejected函数的返回值也能被链式调用接收,作为下一个成功回调的value
  • 在Promise中reject后,没有失败回调函数,或没有catch会throw出reason

修改如下

class MyPromise {
	constructor(executor) {
		if (typeof executor !== 'function') throw new TypeError {
			`Promise resolver ${exector} is not a function`
		}
		
		this.state = 'pending'
		this.value = undefined
		this.reason = undefined
		this.onResolvedCallbacks = []
		this.onRejectedCallbacks = []
		
		const resolve = (value) => {
			const handleResolve = x => {
				if (x instanceof MyPromise) {
					x.then(handleResolve)
				} else {
					this.state = 'fulfilled'
					this.value = x
					this.onResolvedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleResolve(value)
			}
		}
		const reject = (reason) => {
			const handleReject = x => {
				if (x instanceof MyPromise) {
					x.then(handleReject)
				} else {
					this.state = 'rejected'
					this.reason = x
					this.onRejectedCallbacks.forEach(fn => fn())
				}
			}
			if (this.state === 'pending') {
				handleReject(reason)
			}
		}
			executor(resolve, reject)
	}

	then(onfulfilled, onrejected) {
		let p2 = new MyPromise((resolve, reject) => {
			if (this.state === 'fulfilled') {
				let x = onfulfilled(this.value)
				//  设置定时器,保证p2被初始化再执行
				setTimeout(() => {
					if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
					else resolve(x)
				}, 0)
			}
			if (this.state === 'rejected') {
				// 同步时,无失败回调
				if (!onrejected) throw this.reason
				let x = onrejected(this.reason)
				setTimeout(() => {
					if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
					else resolve(x)
				}, 0)
			}
			if (this.state === 'pending') {
				this.onResolvedCallbacks.push(() => {
					let x = onfulfilled(this.value)
					setTimeout(() => {
						if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
						else resolve(x)
					}, 0)
				})
				this.onRejectedCallbacks.push(() => {
					// 异步时,无失败回调
					if (!onrejected) throw this.reason
					let x = onrejected(this.value)
					setTimeout(() => {
						if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
						else resolve(x)
					}, 0)
				})
			}
		})
		return p2
	}
}

ok,成功实现了我们自己的Promise,虽然上面只有一个then方法

给MyPromise添加静态方法

下面将向MyPromise上添加四个静态方法

  • resolve
  • reject
  • race
  • all

resolve

传入一个value,获得包裹该value的成功状态的Promise对象

static resolve = value => {
	return new MyPromise((resolve, reject) => {
		resolve(value)
	})
}

reject

传入一个reason,获得包裹该reason的失败状态的Promise对象

static reject = reason => {
	return new MyPromise((resolve, reject) => {
		reject(reason)
	})
}

race

传入一个Promise数组,获得最先成功(或失败)的Promise对象

static race = promises => {
	return new MyPromise((resolve, reject) => {
		promises.forEach(promise => {
			promise.then(resolve, reject)
		})
	})
}

all

传入一个Promise数组,等其中所有Promise对象状态从pending变为fulfilled时,返回一个Promise对象,该对象的value是Promise数组的所有value构成的数组(按顺序)。
若其中有Promise对象状态变为rejected,就返回一个rejected状态的Promise对象,reason与其中rejected的Promise对象相同

static all = promises => {
	return new MyPromise((resolve, reject) => {
		const valArr = []
		// 计数器
		// 因为使用valArr[x] = y的方式赋值,length不一定等于存入元素数
		let count = 0
		function processData(i, value) {
			// 需要索引值,以保证value的顺序
			valArr[i] = value
			count++
			if (count === promises.length) {
				resolve(valArr)
			}
		}
		promises.forEach((promise, i) => {
			promise.then(value => {
				// 利用闭包保存状态
				processData(i, value)
			}, reject) // 有rejected的promise直接reject了
		})
	})
}


最终的MyPromise.js

// MyPromise.js
class MyPromise {
  constructor(executor) {
    if (typeof executor !== 'function') throw new TypeError(
      `Promise resolver ${executor} is not a function`
    )

    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      const handleResolve = x => {
        if (x instanceof MyPromise) {
          x.then(handleResolve)
        } else {
          this.state = 'fulfilled'
          this.value = x
          this.onResolvedCallbacks.forEach(fn => fn())
        }
      }

      if (this.state === 'pending') {
        handleResolve(value)
      }
    }

    const reject = (reason) => {
      const handleReject = x => {
        if (x instanceof MyPromise) {
          x.then(handleReject)
        } else {
          this.state = 'rejected'
          this.reason = x
          this.onRejectedCallbacks.forEach(fn => fn())
        }
      }

      if (this.state === 'pending') {
        handleReject(reason)
      }
    }

      executor(resolve, reject)
  }

  then(onfulfilled, onrejected) {
    let p2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        let x = onfulfilled(this.value)
        setTimeout(() => {
          if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
          else resolve(x)
        }, 0)
      }
      if (this.state === 'rejected') {
        if (!onrejected) throw this.reason
        let x = onrejected(this.reason)
        setTimeout(() => {
          if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
          else resolve(x)
        }, 0)
      }
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          let x = onfulfilled(this.value)
          setTimeout(() => {
            if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
            else resolve(x)
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          if (!onrejected) throw this.reason
          let x = onrejected(this.reason)
          setTimeout(() => {
            if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
            else resolve(x)
          }, 0)
        })
      }
    })
    return p2
  }

  static resolve = value => {
    return new MyPromise((resolve, reject) => {
      resolve(value)
    })
  }

  static reject = reason => {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static race = promises => {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(resolve, reject)
      })
    })
  }

  static all = promises => {
    return new MyPromise((resolve, reject) => {
      const valArr = []
      let count = 0
      function processData(i, value) {
        valArr[i] = value
        count++
        if (count === promises.length) {
          resolve(valArr)
        }
      }
      promises.forEach((promise, i) => {
        promise.then(value => {
          processData(i, value)
        }, reject)
      })
    })
  }
}

不足之处

本文对Promise的异常,处理得比较粗糙,包括promise对象rejected了,但没有onrejected函数传入也没有抛出错误…只是用了一个简单的方法,实现了then方法没有传入onrejected但promise对象rejected时会报错
肯定和真正的Promise有不少差距,但大体上我想能够帮助你更好地理解Promise

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值