Promise详解-手写Promise,实现一款自己的简易Promise

Promise 是ES6异步编程的一种解决方案:
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:
pending(等待),
fulfiled(成功),
rejected(失败) ;
状态一旦改变,就不会再变。
创造promise实例后,它会立即执行。

让我们一起实现一款自己的Promise,顺便还可以理解理解源码。

Let’s go

首先打开我们的编辑器,新建一个.js格式的文件。声明PaddingFulfiedRejected三种状态,然后创建一个Class,当然你也可以使用构造函数写。
为了方便理解,每行代码都有注释。

//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝

//创建一个名为 MyPromise 的类
class MyPromise {
    //写个构造器方便接受 new 的时候传进来的参数
	// 构造器	
	constructor(executor) {
        // 成功回调的队列
		this.resolveQueue = []		
		// 拒绝/失败的回调队列		             
		this.rejectQueue = []		
		// 储存当前队列的value		  
		this.currentValue = null
     }
}
一、完善构造器
	// 构造器
	constructor(executor) {
		// 成功回调的队列
		this.resolveQueue = []
		// 拒绝/失败的回调队列
		this.rejectQueue = []
		// 储存当前队列的value
		this.currentValue = null
		// 判断executor是否是一个函数
		if (typeof executor !== 'function') {
			throw new Error('MyPromise is not a function')
		}
		// 修改状态为Padding
		this.status = Padding
		// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
		let _resolve = value => {
			/*
			    把resolve执行回调的操作封装成一个函数,
			    放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
			    我们还可以使用MutationObserver
			*/
			let run = () => {
				// 判断并修改状态
				if (this.modifyStatus(Fulfilled)) return
				// 储存当前的value
				this.currentValue = value
				// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空
				/*
				    这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>
				    如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调
				*/
				while (this.resolveQueue.length) {
					// 弹出
					let callback = this.resolveQueue.shift()
					// 执行
					callback(value)
				}
			}
			// 调用
			setTimeout(run);
		}
		// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
		let _reject = value => {
			/*
			    把reject执行回调的操作封装成一个函数,
			    放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
			    我们还可以使用MutationObserver
			*/
			let run = () => {
				// 判断并修改状态
				if (this.modifyStatus(Rejected)) return
				// 储存当前的value
				this.currentValue = value
				// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空
				/*
				    这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>
				    如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调
				*/
				while (this.rejectQueue.length) {
					// 弹出
					let callback = this.rejectQueue.shift()
					// 执行
					callback(value)
				}
			}
			// 调用
			setTimeout(run)
		}
		// 执行
		executor(_resolve, _reject)
	}
1、 then

这个是promise的重要点,除了finally、reject不需要,其他的都需要用到他的原理内容。

	// resolve/then/成功
	then(resolvedFn, rejectedFn) {
		/*
		    根据Promise A+规范,如果 then() 接收的参数不是function,那么我们应该忽略它。
		    如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
		*/
		// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒
		if (resolvedFn && typeof resolvedFn !== 'function') {
			throw new Error('resolver is not a function')
		}
		// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒
		if (rejectedFn && typeof rejectedFn !== 'function') {
			throw new Error('rejecter is not a function')
		}
		// 为了实现.then的链式调用,此时需要返回一个promise
		return new MyPromise((resolve, reject) => {
			/*
			    then
			*/
			//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
			let fulfilledFn = value => {
				try {
					//执行第一个(当前的)Promise的成功回调,并获取返回值
					let x = resolvedFn(value)
					//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
					//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
					x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
				} catch (error) {
					reject(error)
				}
			}
			// 设置对列
			this.resolveQueue.push(fulfilledFn)

			/*
			    reject
			    根据设置resolvedFn的原理,设置一下rejectFn
			*/
			//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
			let rejectFn = value => {
				try {
					//执行第一个(当前的)Promise的成功回调,并获取返回值
					let x = rejectedFn(value)
					//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
					//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
					x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
				} catch (error) {
					reject(error)
				}
			}
			// 设置对列
			this.rejectQueue.push(rejectFn)

			/*
			    处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,
			    有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),
			    如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,
			    因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调
			*/
			// 当前状态判断
			switch (this._status) {
				// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行
				case Padding:
					this.resolveQueue.push(fulfilledFn)
					this.rejectQueue.push(rejectFn)
					break;
					// 当状态已经变为resolve/reject时,直接执行then回调
				case Fulfilled:
					fulfilledFn(this.currentValue)
					break;
				case Rejected:
					rejectFn(this.currentValue)
					break;
			}
		})

	}
2、 catch
	// reject/catch 失败
	catch (rejectedFn) {
		// 判断是否是一个函数
		if (typeof rejectedFn === 'function') {
			return this.then(null, rejectedFn)
		}
	}
3、 resolve
	// resolve 
	/*
	    resolve(resolver)方法返回一个以给定值解析后的Promise 对象。
	    如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
	    返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
	    否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
	*/
	static resolve(resolver) {
		// 根据规范, 如果参数是Promise实例, 直接return这个实例
		if (resolver instanceof MyPromise) return resolver
		return new MyPromise(resolve => resolve(resolver))
	}
4、 reject
	// reject
	/* reject()方法返回一个带有拒绝原因的Promise对象。 */
	static reject(reason) {
		return new MyPromise((resolve, reject) => reject(reason))
	}
5、 finally

finally 无论成功失败都会返回,该方法与状态无关

	// finally 无论成功失败都会返回-该方法与状态无关
	finally(finallyFn) {
		return this.then(
			// resolve执行回调,并在then中return结果传递给后面的Promise
			value => MyPromise.resolve(callback()).then(() => value),
			// resolve执行回调,并在then中return结果传递给后面的Promise
			reason => MyPromise.resolve(callback()).then(() => {
				throw reason
			})
		)
	}
6、 all

Promise.all方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成**(resolved)**
或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),
此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

	/*
        Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
		或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),
        此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
    */
	static all(promiseArr) {
		let index = 0
		let result = []
		return new MyPromise((resolve, reject) => {
			promiseArr.forEach((item, itemIndex) => {
				//resolve(item)用于处理传入的值不为Promise的情况
				MyPromise.resolve(item).then(
					val => {
						index++
						result[itemIndex] = value
						//所有then执行后, resolve结果
						if (index === promiseArr.length) {
							resolve(result)
						}
					}
					,
					//有一个Promise被reject时,MyPromise的状态变为reject
					err => reject(err)
				)
			})
		})
	}
7、 race

race方法返回一个 promise
一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。

	/*
	    race(iterable)方法返回一个 promise,
	    一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。
	*/
	static race(promiseArr) {
		return new MyPromise((resolve, reject) => {
			//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
			for (let item of promiseArr) {
				MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况
					value => resolve(value),
					err => reject(err)
				)
			}
		})
	}
二、 关于修改状态

我这里是把修改状态单独拿出来的,代码如下:

	// 修改状态
	modifyStatus(status) {
		/* 
			因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去
			这里return 为true是因为上面通过这个做过状态校验判断
		*/
		if (this.status !== Padding) return true
		// 修改状态
		this.status = status
	}
三、 完整代码

不想复制阅读的同学可以点击 -> Promise 原理/实现自己的Promise
进行下载。使用的时候就像我们日常使用Promise那样使用就OK了,只是我们需要导入这个js

//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝

// 声明类
class MyPromise {
	// 构造器
	constructor(executor) {
		// 成功回调的队列
		this.resolveQueue = []
		// 拒绝/失败的回调队列
		this.rejectQueue = []
		// 储存当前队列的value
		this.currentValue = null
		// 判断executor是否是一个函数
		if (typeof executor !== 'function') {
			throw new Error('MyPromise is not a function')
		}
		// 修改状态为Padding
		this.status = Padding
		// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
		let _resolve = value => {
			/*
			    把resolve执行回调的操作封装成一个函数,
			    放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
			    我们还可以使用MutationObserver
			*/
			let run = () => {
				// 判断并修改状态
				if (this.modifyStatus(Fulfilled)) return
				// 储存当前的value
				this.currentValue = value
				// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空
				/*
				    这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>
				    如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调
				*/
				while (this.resolveQueue.length) {
					// 弹出
					let callback = this.resolveQueue.shift()
					// 执行
					callback(value)
				}
			}
			// 调用
			setTimeout(run);
		}
		// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数
		let _reject = value => {
			/*
			    把reject执行回调的操作封装成一个函数,
			    放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,
			    我们还可以使用MutationObserver
			*/
			let run = () => {
				// 判断并修改状态
				if (this.modifyStatus(Rejected)) return
				// 储存当前的value
				this.currentValue = value
				// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空
				/*
				    这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>
				    如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调
				*/
				while (this.rejectQueue.length) {
					// 弹出
					let callback = this.rejectQueue.shift()
					// 执行
					callback(value)
				}
			}
			// 调用
			setTimeout(run)
		}
		// 执行
		executor(_resolve, _reject)
	}
	// resolve/then/成功
	then(resolvedFn, rejectedFn) {
		/*
		    根据规范,如果 then() 接收的参数不是function,那么我们应该忽略它。
		    如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
		*/
		// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒
		if (resolvedFn && typeof resolvedFn !== 'function') {
			throw new Error('resolver is not a function')
		}
		// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒
		if (rejectedFn && typeof rejectedFn !== 'function') {
			throw new Error('rejecter is not a function')
		}
		// 为了实现.then的链式调用,此时需要返回一个promise
		return new MyPromise((resolve, reject) => {
			/*
			    then
			*/
			//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
			let fulfilledFn = value => {
				try {
					//执行第一个(当前的)Promise的成功回调,并获取返回值
					let x = resolvedFn(value)
					//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
					//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
					x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
				} catch (error) {
					reject(error)
				}
			}
			// 设置对列
			this.resolveQueue.push(fulfilledFn)

			/*
			    reject
			    根据设置resolvedFn的原理,设置一下rejectFn
			*/
			//把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
			let rejectFn = value => {
				try {
					//执行第一个(当前的)Promise的成功回调,并获取返回值
					let x = rejectedFn(value)
					//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
					//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
					x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
				} catch (error) {
					reject(error)
				}
			}
			// 设置对列
			this.rejectQueue.push(rejectFn)

			/*
			    处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,
			    有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),
			    如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,
			    因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调
			*/
			// 当前状态判断
			switch (this._status) {
				// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行
				case Padding:
					this.resolveQueue.push(fulfilledFn)
					this.rejectQueue.push(rejectFn)
					break;
					// 当状态已经变为resolve/reject时,直接执行then回调
				case Fulfilled:
					fulfilledFn(this.currentValue)
					break;
				case Rejected:
					rejectFn(this.currentValue)
					break;
			}
		})

	}
	// reject/catch 失败
	catch (rejectedFn) {
		// 判断是否是一个函数
		if (typeof rejectedFn === 'function') {
			return this.then(null, rejectedFn)
		}
	}
	// finally 无论成功失败都会返回-该方法与状态无关
	finally(finallyFn) {
		return this.then(
			// resolve执行回调,并在then中return结果传递给后面的Promise
			value => MyPromise.resolve(callback()).then(() => value),
			// resolve执行回调,并在then中return结果传递给后面的Promise
			reason => MyPromise.resolve(callback()).then(() => {
				throw reason
			})
		)
	}
	// resolve 
	/*
	    resolve(resolver)方法返回一个以给定值解析后的Promise 对象。
	    如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),
	    返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
	    否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
	*/
	static resolve(resolver) {
		// 根据规范, 如果参数是Promise实例, 直接return这个实例
		if (resolver instanceof MyPromise) return resolver
		return new MyPromise(resolve => resolve(resolver))
	}
	// reject
	/* reject()方法返回一个带有拒绝原因的Promise对象。 */
	static reject(reason) {
		return new MyPromise((resolve, reject) => reject(reason))
	}
	// all
	/*
        Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”
		或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),
        此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
    */
	static all(promiseArr) {
		let index = 0
		let result = []
		return new MyPromise((resolve, reject) => {
			promiseArr.forEach((item, itemIndex) => {
				//resolve(item)用于处理传入的值不为Promise的情况
				MyPromise.resolve(item).then(
					val => {
						index++
						result[itemIndex] = value
						//所有then执行后, resolve结果
						if (index === promiseArr.length) {
							resolve(result)
						}
					}
					,
					//有一个Promise被reject时,MyPromise的状态变为reject
					err => reject(err)
				)
			})
		})
	}
	// race
	/*
	    race(iterable)方法返回一个 promise,
	    一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。
	*/
	static race(promiseArr) {
		return new MyPromise((resolve, reject) => {
			//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
			for (let item of promiseArr) {
				MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况
					value => resolve(value),
					err => reject(err)
				)
			}
		})
	}
	// 修改状态
	modifyStatus(status) {
		/* 
			因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去
			这里return 为true是因为上面通过这个做过状态校验判断
		*/
		if (this.status !== Padding) return true
		// 修改状态
		this.status = status
	}
}

你可能感兴趣的:(JavaScript,Promise,javascript,前端,面试)