欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

进一步理解并实现一个简单的Promise

程序员文章站 2022-07-03 09:32:08
...

本文将带领大家实现一个简单的Promise,在阅读本文之前,默认您对Promise有基本的了解,会基本的使用,以及对ES6的语法有一定的了解

1. Promise的构造函数

首先,我们从Promise的构造函数开始,这是使用Promise最关键的一步

// 创建三个常量,用于表示Promise的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// Promise类
class Promise {
	// Promise构造函数会传入一个方法
	constructor(executor) {
		// Promise中存在一个保存状态和保存状态结果的变量
		this.promiseState = PENDING
		this.promiseResult = null
		// 创建Promise要传入的两个函数,用于将状态变为成功和失败的函数
		const resolve = res => {
			// 我们使用了箭头函数,方便绑定this
			// 成功的函数,用于将Promise状态修改为FULFILLED,并保存成功值
			// 我们知道,如果Promise状态已改变,就不能再改变了,所以要做一个检测
			if (this.promiseState !== PENDING) return
			this.promiseState = FULFILLED
			this.promiseResult = res
		}
		const reject = err => {
	      	if (this.promiseState !== PENDING) return
	      	this.promiseState = REJECTED
	      	this.promiseResult = err
	    }
	    // 最后,我们需要调用executor,传入两个改变状态的函数,并进行异常处理
	    try {
	    	executor(resolve, reject)
	    } catche (err) {
	    	// 出现异常,我们就调用reject函数,改变Promise状态为失败即可
	    	reject(err)
	    }
	}
}

上面,我们就实现了一个基本的构造函数,但是,上面还只能处理executor是同步改变状态的情况,如果是异步调用的resolve或reject,则不行,我们后续在编写then函数时会完善这个功能

2. 静态方法resolve

因为在then函数中,我们将使用这个方法,所以首先编写这个函数
在编写之前,我们要知道Promise.resolve函数对于不同传入参数的处理,这个函数是用来包装一个值称为Promise对象,但根据不同的参数值传入,会进行不同的处理

1、传入的是一个Promise对象
如果传入的是一个Promise对象,那么将会幂等的返回一个Promise对象,其实意思就是返回这个Promise对象本身
2、传入的是一个thenable对象
thenable对象的含义就是,对象中拥有then方法,此时,这个then方法就跟Promise构造函数传入的executor拥有同样的功能,即这个then方法在参数中会分别接收到resolve和reject方法,可以用来改变Promise对象的状态
3、传入其他的值
如果不是以上两种类型,Promise会创建一个新的Promise对象,将这个新Promise的状态改变为fulfilled,并将状态结果设置为传入的值,即Promise会包装这个值

有了以上的分析,我们就来写一写这个方法

class Promise {
	static resolve(wrapped) {
		if (wrapped instanceof Promise) {
			// 如果是Promise对象
			return wrapped
		} else if (wrapped instanceof Object && wrapped.then instanceof Function) {
			// 是一个thenable对象,then方法就相当于executor函数
			return new Promise(wrapped.then)
		} else {
			// 其他值直接包装
			return new Promise(resolve => resolve(wrapped))
		}
	}
}

当然,上面代码如果要正常运行,得在我们的Promise实现完成之后,因为代码中使用到了Promise,我们之所以先编写这个函数,是为了方便then函数的编写

3. 静态方法reject

既然我们都编写了resolve,那干脆reject的一起写了吧,reject并不像resolve函数一样,resolve函数是幂等的,但reject函数,不论接收到什么值,都会将这个值包装成一个拒绝的Promise对象,话不多说,直接写上代码

class Promise {
	static reject(wrapped) {
		return new Promise((resolve, reject) => reject(wrapped))
	}
}

一行代码就搞定,非常简单是吧

4. then方法,Promise的核心

上面都只是开胃菜,then方法才是我们的重中之重,在书写then方法前,我们先理一理then方法中的一些规则

1、首先,then方法可以传入两个方法,第一个是解决执行的回调,第二个是拒绝执行的回调
2、then方法返回一个Promise对象,根据解决或拒绝的回调函数的返回值决定这个返回的Promise对象的状态
3、解决或拒绝函数中的异常,会导致返回的Promise对象为拒绝状态
4、值传递和异常穿透(后面写的时候会解释)

接下来,我们以代码注释的形式,在代码中将这些功能体现出来,包括一些异步状态改变处理,都会在代码注释中解释

class Promise {
	// 首先是传入参数,一共是两个,实现了我们说的第一条规则
	then(onFulfilled, onRejected) {
		// 这里创建了一个新的Promise对象,作为函数的返回值
		// 这个Promise对象的状态,由参数的两个处理函数的返回值决定
		// 我们在函数最后返回了这个值,实现了上述的第二条规则
		const resultPromise = new Promise((resolve, reject) => {
			// 此处便实现了第三条规则的值传递
			// 如果传入的解决的回调函数参数不是一个函数
			// 那么返回的Promise对象的状态和值便需要同步未处理的值
			// 以便值能传递到这个返回的Promise对象的解决的then方法的回调中
			if (typeof onFulfilled !== 'function ') {
				onFulfilled = resolve
			}
			// 实现异常穿透跟值传递也是一样
			// 需要将异常传递到下一个then函数的回调中去
			if (typeof onRejected !== 'function ') {
				onRejected = reject
			}
			// 接下来就是对于这个两个回调函数的调用
			if (this.promiseState === PENDING) {
				// 建议可以先看一下下面解决和拒绝状态的处理,再来看等待状态
				// 如果执行到then方法,状态还是等待,说明状态是异步改变的
				// 此时我们就需要把成功和失败的回调保存下来,等到状态改变的时候再调用
				// 我们就可以在构造函数中多创建一个数组,用来专门保存这个要异步执行的回调函数
				// 需要在构造函数中改变的代码,将在then方法后面写出
				this.promiseCallbacks.push({
					// 这个onFulfilled只是对象的键名,不要和then传入的onFulfilled混淆了
					onFulfilled: () => {
						let value
						try {
							// 此处调用的是then传入的onFulfilled
							value = onFulfilled(this.promiseResult)
						} catch (err) {
							// 如果出现异常,那么就把返回的Promise的状态同步为拒绝
							reject(err)
						}
						Promise.resolve(value).then(resolve, reject)
					},
					onRejected: () => {
						let value
						try {
							// 此处调用的是then传入的onRejected
							value = onRejected(this.promiseResult)
						} catch (err) {
							// 如果出现异常,那么就把返回的Promise的状态同步为拒绝
							reject(err)
						}
						Promise.resolve(value).then(resolve, reject)
					}
				})
			} else if (this.promiseState === Promise.FULFILLED) {
				// 如果在执行then方法中回调时,状态是解决,直接执行回调
				// 当然执行回调要进行异常处理,以及返回Promise状态的同步
				let value
				try {
					value = onFulfilled(this.promiseResult)
				} catch (err) {
					// 如果出现异常,那么就把返回的Promise的状态同步为拒绝
					reject(err)
				}
				// 我们此处就巧妙的使用静态方法resolve,来同步返回的Promise的值和状态
				// 以为then传入的回调函数的返回值的处理,和resolve的处理是一样
				// 所以我们可以使用resolve包装这个返回值,都会返回每种情况包装后的Promise对象
				// 然后我们就只需要使用then方法,再将返回的Promise对象的resolve和reject传入then中
				// 那么就能实现返回的Promise对象的状态同步then方法回调函数的返回值的情况
				Promise.resolve(value).then(resolve, reject)
			} else if (this.promiseState === Promise.REJECTED) {
				// 状态为拒绝的情况,跟上面一样,除了调用的回调函数不一样
		        let value
				try {
					value = onRejected(this.promiseResult)
				} catch (err) {
					reject(err)
				}
				Promise.resolve(value).then(resolve, reject)
	      	}
		})
		return resultPromise
	}
	
	// 这里是上述改变后的构造函数的代码,我们删除了不必要的注释
	constructor(executor) {
		this.promiseState = PENDING
		this.promiseResult = null
		// 这里就加入一个保存回调的数组,用于保存回调函数
		// 您可能会疑惑为什么要用一个数组来保存
		// 因为一个Promise对象可以多次调用then方法,所以就可以又多个回调函数需要保存
		this.promiseCallbacks = []
		const resolve = res => {
			if (this.promiseState !== PENDING) return
			this.promiseState = FULFILLED
			this.promiseResult = res
			// 成功的处理就应该调用所有成功的回调函数
			this.promiseCallbakcs.forEach(item => item.onFulfilled())
		}
		const reject = err => {
	      	if (this.promiseState !== PENDING) return
	      	this.promiseState = REJECTED
	      	this.promiseResult = err
	      	// 失败的处理就应该调用所有失败的回调函数
			this.promiseCallbakcs.forEach(item => item.onRejected())
	    }
	    try {
	    	executor(resolve, reject)
	    } catche (err) {
	    	reject(err)
	    }
	}
}

看了上面代码,咱们可能发现,有需要重复的代码,比如在处理异常和同步状态的地方,有些非常高的相似度,所以我们将代码优化一下,将重复的地方封装一下

// 首先,我们也是将不必要的注释删除,并将能单行书写的代码也改为单行
class Promise {
	then(onFulfilled, onRejected) {
		const resultPromise = new Promise((resolve, reject) => {
			// 此处我们写一个函数,整合异常处理和返回值同步的地方
			// 参数handler代码要执行的函数,如onFulfilled或onRejected
			const resultHandler = handler => {
				// 这里使用一个setTimeout,就是模拟了then方法回调函数的异步执行
				// 在Promise中then方法的回调函数是异步的微任务,我们这里用这个模拟一下异步
				setTimeout(() => {
					// 这里面的结构就跟之前的处理是一样的
					// 只是把要调用的函数,换成了传入的handler
					let value
					try {
						value = handler(this.promiseResult)
					} catch (err) {
						reject(err)
					}
					// 这里我们加入一个循环引用的检测
					// 防止then回调函数中返回自己返回的Promise对象
					if (callbackReturn === returnPromise) {
		            	throw new TypeError('Chaining cycle detected for promise #<Promise>')
		          	}
					Promise.resolve(value).then(resolve, reject)
				})
			}
			if (typeof onFulfilled !== 'function ') onFulfilled = resolve
			if (typeof onRejected !== 'function ') onRejected = reject
			if (this.promiseState === PENDING) {
				this.promiseCallbacks.push({
					// 下面这些地方就会变得很简单,只能传入对应的处理函数即可
					onFulfilled: () => resultHandler(onFulfilled)
					onRejected: () => resultHandler(onRejected)
				})
			} else if (this.promiseState === Promise.FULFILLED) {
				resultHandler(onFulfilled)
			} else if (this.promiseState === Promise.REJECTED) {
		        resultHandler(onRejected)
	      	}
		})
		return resultPromise
	}
}

到这里,是不是发现其实也不难,只要对Promise功能有一定的了解,相信大家自己总结一下也能够将它写出来

5. catch方法

有了上面的函数,剩下的方法实现起来都会变得非常容易

class Promise {
	catch(onRejected) {
		// catch方法其实就是用来处理异常穿透下来的异常,所以它跟then方法中onRejected的功能是一样的
		// 所以我们可以直接使用then方法来完成它的功能
		return this.then(null, onRejected)
	}
}

6. finally方法

class Promise {
	finally(onFilnally) {
		// finally也同样,但是无论解决还是拒绝,都调用同样的方法
		// 但是finally回调函数不需要传入值,所以使用一个箭头函数包装一下
		return this.then(() => onFilnally(), () => onFilnally())
	}
}

7. 静态方法all

all方法可以接收一个数组,其中的值如果是Promise对象,则只有当所有对象是解决状态时,这个all方法才会返回一个解决的值,如果有任何一个Promsie对象被拒绝,那么all方法返回的Promise也是拒绝的,并且拒绝理由就是这个被拒绝的Promise的理由,如果参数数组中有的元素不是Promise对象,则会被包装成Promise对象

class Promise {
	static all(promises) {
		// 我们就默认传入的是数组,省去一些检查的步骤
		return new Promise((resolve, reject) => {
			// 放置结果的数组
			let ret = []
			// 用于判断是否数组中所有promise对象都处理完毕
			let count = 0
			// 我们就应该遍数组,去处理数组中的每一个Promise对象
			for (let index = 0; index < promises.length; ++index) {
				// 我们使用Promise.resolve包装值,让所有元素都称为Promise对象
				Promise.resolve(promises[index]).then(
					res => {
						// 如果成功,存储其成功值
						count++
						ret[index] = res
						if (count === promises.length) {
							// 如果全部都解决,则返回的Promise为解决
							resolve(ret)
						}
					}, err => {
						// 如果有任何一个失败,则返回的Promise为失败
						reject(err)
					}
				)
			}
		})
	}
}

8. 静态方法race

race方法相对all就更加简单,谁快就是谁

class Promise {
	static race(promises) {
		// 我们就默认传入的是数组,省去一些检查的步骤
		return new Promise((resolve, reject) => {
			for (let index = 0; index < promises.length; ++index) {
				Promise.resolve(promises[index]).then(
					res => {
						resolve(res)
					}, err => {
						reject(err)
					}
				)
			}
		})
	}
}

9. 整理所有代码

下面列出所有代码的整合,删除了注释

class Promise {
	constructor(executor) {
		this.promiseState = PENDING
		this.promiseResult = null
		this.promiseCallbacks = []
		const resolve = res => {
			if (this.promiseState !== PENDING) return
			this.promiseState = FULFILLED
			this.promiseResult = res
			this.promiseCallbakcs.forEach(item => item.onFulfilled())
		}
		const reject = err => {
	      	if (this.promiseState !== PENDING) return
	      	this.promiseState = REJECTED
	      	this.promiseResult = err
			this.promiseCallbakcs.forEach(item => item.onRejected())
	    }
	    try {
	    	executor(resolve, reject)
	    } catche (err) {
	    	reject(err)
	    }
	}
	
	then(onFulfilled, onRejected) {
		const resultPromise = new Promise((resolve, reject) => {
			const resultHandler = handler => {
				setTimeout(() => {
					let value
					try {
						value = handler(this.promiseResult)
					} catch (err) {
						reject(err)
					}
					if (callbackReturn === returnPromise) {
		            	throw new TypeError('Chaining cycle detected for promise #<Promise>')
		          	}
					Promise.resolve(value).then(resolve, reject)
				})
			}
			if (typeof onFulfilled !== 'function ') onFulfilled = resolve
			if (typeof onRejected !== 'function ') onRejected = reject
			if (this.promiseState === PENDING) {
				this.promiseCallbacks.push({
					onFulfilled: () => resultHandler(onFulfilled)
					onRejected: () => resultHandler(onRejected)
				})
			} else if (this.promiseState === Promise.FULFILLED) {
				resultHandler(onFulfilled)
			} else if (this.promiseState === Promise.REJECTED) {
		        resultHandler(onRejected)
	      	}
		})
		return resultPromise
	}
	
	static resolve(wrapped) {
		if (wrapped instanceof Promise) {
			return wrapped
		} else if (wrapped instanceof Object && wrapped.then instanceof Function) {
			return new Promise(wrapped.then)
		} else {
			return new Promise(resolve => resolve(wrapped))
		}
	}
	
	static reject(wrapped) {
		return new Promise((resolve, reject) => reject(wrapped))
	}

	catch(onRejected) {
		return this.then(null, onRejected)
	}

	finally(onFilnally) {
		return this.then(() => onFilnally(), () => onFilnally())
	}

	static all(promises) {
		return new Promise((resolve, reject) => {
			let ret = []
			let count = 0
			for (let index = 0; index < promises.length; ++index) {
				Promise.resolve(promises[index]).then(res => {
					count++
					ret[index] = res
					if (count === promises.length) resolve(ret)
				}, err => {
					reject(err)
				})
			}
		})
	}
	
	static race(promises) {
		return new Promise((resolve, reject) => {
			for (let index = 0; index < promises.length; ++index) {
				Promise.resolve(promises[index]).then(res => {
					resolve(res)
				}, err => {
					reject(err)
				})
			}
		})
	}
}

10. 总结

到此为止,我们就实现了一个简单版本的Promise,相信在大家思考和练习之后,会发现,其实也不难嘛,也希望大家能从文章中学到东西,如果文章中有任何错误的地方,望各位大牛们能指点,谢谢大家。