手写promise1,分步详解

117 阅读13分钟

promise

promise的基础

promise 接受一个执行器,该执行有两个参数一个参数是成功时执行的函数resolve,一个是失败是执行的函数reject

promise 有三种状态分别是pendding(等待)、fulfilled(成功)、rejected(失败)。

状态 pendding ----> fulfilled 或者 pendding ----> rejected 只会发生一种状态改变并且状态一旦改变就不会再变

resolve函数和reject函数会改变状态

// 首先设置三种状态常量
const PENDDING = 'pendding';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class Mypromise {
	constructor(executor) {
		// new promise是会立即执行执行器,并且接受两个参数, resolve函数和reject函数
		executor(resolve, reject)
	}
	
	// resolve是一个函数
	resolve = () => {
		
	}
	// reject也是一个函数
	reject= () => {}

}

我们需要有一个status用来获取当前执行时的状态,

resolve函数执行时候将当前状态改为fulfilled

reject函数执行时候将当前状态改为rejected

	constructor(executor) {
		// new promise是会立即执行执行器,并且接受两个参数, resolve函数和reject函数
		// resolve和reject是实例属性
		executor(this.resolve, this.reject)
	}
	// 当前状态	实例化是pendding
	status = PENDDING; 
	// resolve是一个函数, 参数是响应值
	resolve = (response) => {
		this.status = FULFILLED; // 修改状态FULFILLED
	}
	// reject也是一个函数, 参数是错误消息
	reject= (errmessage) => {
		this.status = REJECTED; // 修改状态REJECTED
	}
	
	// 每个实例都可以调用then函数, 该函数接受两个回调函数,
	then = (successFn, failureFn) => {
		// 当前状态成功了执行successFn
		// 当前状态失败了执 failureFn
		if(this.status === FULFILLED) {
			successFn() 
		} else if(this.status === REJECTED) {
			failureFn()
		}
	}

then 第一个回调函数接收到resolve中的响应数据, 第二个回调函数接收reject中的失败信息

因此我们要在实例化的时候存储响应数据和失败信息


response = undefined; // 定义一个成功的响应值
errmessage = undefined // 失败的信息
// resolve是一个函数, 参数是响应值 
resolve = (response) => { 
	this.status = FULFILLED; 
	this.response = response; // 缓存成功消息
} 
// reject也是一个函数, 参数是错误消息 
reject= (errmessage) => { 
	this.status = REJECTED; 
	this.errmessage = errmessage // 缓存失败信息
} 
then = (successFn, failureFn) => { 
	// 当前状态成功了执行successFn 
	// 当前状态失败了执行 failureFn 
	if(this.status === FULFILLED) { 
		successFn(this.response) // 执行成功的回调 参数是成功的响应数据
 	} else if(this.status === REJECTED) {  
		failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
	} 
}
const p = new Mypromise((resolve, reject) => {
	resolve('成功')
	reject('失败')
})
p.then(res=> {
	console.log(res) 
}, err=> {
	console.log(err) // 失败 很明显这里打印了失败,说明状态 PENDDING ---- FULFILLED ----REJECTED;
})
// 失败 很明显这里打印了失败,说明状态 PENDDING ---- FULFILLED ----REJECTED;
// 分析一下 状态只能 由pendding --- fulfilled 或者 pendding --- rejected
resolve = (response) => { 
	// 这里刚开始执行状态如果不是pendding return出去
	if(this.status !== PENDDING ) return
	this.status = FULFILLED; 
	this.response = response; // 缓存成功消息
} 
// reject也是一个函数, 参数是错误消息 
reject= (errmessage) => { 
	// 同理
	if(this.status !== PENDDING ) return
	this.status = REJECTED; 
	this.errmessage = errmessage // 缓存失败信息
} 
const p = new Mypromise((resolve, reject) => {
	resolve('成功')
	reject('失败')
})
p.then(res=> {
	console.log(res) // 成功
}, err=> {
	console.log(err) // 程序不走这里说明状态只会改变依次
})

处理异步

promise是处理异步操作。。。 如果程序仅仅是这样,那么加入了异步操作会怎样?


////////
const p = new Mypromise((resolve, reject) => {
	setTimeout(() => {
        resolve('hello')
    }, 2000)
})
p.then(res=> {
	console.log(res) // 不走
}, err=> {
	console.log(err) // 不走
})

// 分析then:
// 1. 走到then函数中, this.status 此时是 pendding。 因为2s之后才会执行resolve,才会改变状态
then = (successFn, failureFn) => { 
	// 当前状态成功了执行successFn 
	// 当前状态失败了执行 failureFn 
	if(this.status === FULFILLED) { 
		successFn(this.response) // 执行成功的回调 参数是成功的响应数据
 	} else if(this.status === REJECTED) {  
		failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
	} 
}

    因此then中需要考虑 如果是pendding 状态 ;

    2s之后如果是fulfilled状态了 我们应该再走successFn; 如果是rejected状态走 failureFn

    那么我们如何在2s之后的resolve中拿到 successFn ; 如果在2s之后的reject中拿到 failureFn

// 首先我们定义属性succcessCallback
succcessCallback = undefined;
failureCallback = undefined;
then = (successFn, failureFn) => { 
	// 当前状态成功了执行successFn 
	// 当前状态失败了执行 failureFn 
	if(this.status === FULFILLED) { 
		successFn(this.response) // 执行成功的回调 参数是成功的响应数据
 	} else if(this.status === REJECTED) {  
		failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
	} else { 
		// 如果在1ms执行到这里还是 pendding状态我们 储存successFn和 failureFn
		this.succcessCallback = successFn;
		this.failureCallback = failureFn;
	}
}


// 然后在resolve函数中
resolve = (response) => { 
	// 这里刚开始执行状态如果不是pendding return出去
	if(this.status !== PENDDING ) return
	this.status = FULFILLED; 
	this.response = response; // 缓存成功消息
	// 当异步操作2s后执行到resolve的时候 如果succcessCallback 存在那么执行succcessCallback 
	this.succcessCallback && this.succcessCallback(this.response)
} 
// 在reject函数中
reject= (errmessage) => { 
	// 同理
	if(this.status !== PENDDING ) return
	this.status = REJECTED; 
	this.errmessage = errmessage // 缓存失败信息
	// 当异步操作2s后执行到reject的时候 如果failureCallback 存在那么执行failureCallback 
	this.failureCallback && this.failureCallback (this.errmessage )
} 

const p = new Mypromise((resolve, reject) => {
	setTimeout(() => {
        resolve('hello')
    }, 2000)
})
p.then(res=> {
	console.log(res) // 2s后打印hello
}, err=> {
	console.log(err) // 不走
})

异步连续调用

const p = new Mypromise((resolve, reject) => {
	setTimeout(() => {
        resolve('111')
    }, 2000)
})
p.then(res=> {
	console.log(res)
}, err=> {
	console.log(err)
})
p.then(res=> {
	console.log(res + 'hi')
})
p.then(res=> {
	console.log(res + 'say')
})

如上 then的连续调用我们如何解决呢


我们在then的异步操作中,我们可以将回调函数存储起来,在2s之后执行resolve函数时依次拿到存储的回调函数然后执行

    succcessCallback = []; // 用数组进行储存所有成功回调
    failureCallback = [];	// 用数组进行储存所有失败回调
	resolve = (response) => { 
        if(this.status !== PENDDING ) return
    	this.status = FULFILLED; 
    	this.response = response; // 缓存成功消息
        // 当异步操作2s后执行到resolve的时候 如果succcessCallback 存在那么执行succcessCallback 
    	//this.succcessCallback && this.succcessCallback(this.response)
		// 判断长度如果不为空, 用shift拿到数组的首个cb去执行
        while (this.succcessCallback.length) this.succcessCallback.shift()(this.response)
    } 
    // reject也是一个函数, 参数是错误消息 
    reject= (errmessage) => { 
        if(this.status !== PENDDING ) return
    	this.status = REJECTED; 
    	this.errmessage = errmessage // 缓存失败信息
        // 当异步操作2s后执行到reject的时候 如果failureCallback 存在那么执行failureCallback 
    	//this.failureCallback && this.failureCallback (this.errmessage )
		// 判断长度如果不为空, 用shift拿到数组的首个cb去执行
        while (this.succcessCallback.length) this.failureCallback.shift()(this.errmessage)
    } 
	then = (successFn, failureFn) => { 
    	// 当前状态成功了执行successFn 
    	// 当前状态失败了执行 failureFn 
    	if(this.status === FULFILLED) { 
    		successFn(this.response) // 执行成功的回调 参数是成功的响应数据
     	} else if(this.status === REJECTED) {  
    		failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
    	} else {
            // 如果是pendding状态我们首先储存所有succcessCallback  和 failureCallback 
    		this.succcessCallbac.push(successFn);
    		this.failureCallback.push(failureFn);
        }
    }
// 2s后打印如下
hello
hellohi
hellosay

then后面还可以链式调用then方法

  1. then的链式调用说明then return了一个promise

  2. return的promise根据promise的执行结果去链式调用下一个then

  3. 如果promise的结果时reolve 并且时一个普通值 ?

  4. 如果promise的结果时reolve 并且是一个promise对象?

	then = (successFn, failureFn) => { 
        // 实例化pr
        const pr = new Mypromise((resolve, reject) => {
            // 当前状态成功了执行successFn 
	    	// 当前状态失败了执行 failureFn 
	    	if(this.status === FULFILLED) { 
            
	    		let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
	         	resolvePromise(res, resolve, reject) // 处理成功时结果
	        } else if(this.status === REJECTED) {  
	    		failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
	    	} else {
            // 如果是pendding状态我们首先储存succcessCallback  和 failureCallback 
    		this.succcessCallback.push(successFn);
    		this.failureCallback.push(failureFn);
	        }
	    })
    	
        return pr; // 返回一个promise
    }

------------------------------------
// 如果successFn执行 res=> {} 如果返回一个普通值 ,那么下一个链式then的 res 就是这个值
.then(res => {
})

// 如果successFn执行 res=> {} 如果返回promise对象,那么下一个链式then的 res 就是promise的结果
.then(res => {
})

----------------------------
// res是successFn执行的结果, resolve是成功的回调, reject是结束的回调

function resolvePromise(res, resolve, reject) {
    if (res instanceof Mypromise) {
        // 执行结果是一个promise
        // 调用then方法拿到执行结果如果成功执行resolve
        // 失败就调用reject
        // res.then(value => resolve(value), err => reject(err))
		res.then(resolve(value),reject(err))
    } else {
        // 普通值直接调用resolve方法
        resolve(res)
    }
}

const p = new Mypromise((resolve, reject) => {
	// setTimeout(() => {
 //        resolve('hello')
 //    }, 2000)
	 resolve('hello')
})
p.then(res=> {
	console.log(res) // 打印hello
    return 1000
}, err=> {
	console.log(err)
}).then(res => {
    console.log(res) // 打印1000
})

promise链式调用捕获错误

  1. 如果实例化Mypromise发生了错误, 应当在then执行时去执行错误回调failureFn
constructor(executor) {
		// new promise是会立即执行执行器,并且接受两个参数, resolve函数和reject函数
		// 执行器执行时发生了错误用try cathch去捕获执行器执行
		try {
			executor(this.resolve, this.reject)
			
		} catch (e) {
			// 如果有错误直接执行reject
			this.reject(e)
		}
		
	}

const p = new Mypromise((resolve, reject) => {
	throw new Error('executor错误') // 抛出一个错误
})
p.then(res=> {
	console.log(res)
    return 1000
}, err=> {
	console.log(err  '---') // Error: executor错误---  failureFn回调执行
}).then(res => {
    console.log(res)
})
  1. 如果then中发生了错误需要在链式的下一个then中去执行failureFn
 // then中发生的错误 也用try catch捕获
	try {
		let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
		resolvePromise(res, resolve, reject) // 处理成功时结果
	} catch(e) {
		reject(e)
	}
const p = new Mypromise((resolve, reject) => {
	// throw new Error('executor错误')
	resolve('CHENGGONG')
})
p.then(res=> {
	console.log(res)
    throw new Error('then中错误') // 如果then中发生了错误
}, err=> {
	console.log(err + '---')
}).then(res => {
    console.log(res)
}, err => {
	console.log('捕获then')
	console.log(err)
})		

// 打印结果	
//CHENGGONG
//捕获then
//  Error: then中错误

关于then失败的链式调用

目前为止 then成功时 可以进行链式调用,并且可以捕获到执行器发生的错误和then成功时发生错误

那么失败我们还不能链式调用。 也没有捕获到then失败的发生错误

if(this.status === REJECTED) {  // 如果失败了我们也应该去拿到失败回调的结果进行 通过resolvePromise, 解析失败的回调,并且用try cath捕获失败回调发生的错误
	try {
		let res = failureFn(this.errmessage ) // 执行失败的回调 参数是失败信息
		resolvePromise(res, resolve, reject) // 处理成功时结果
	} catch(e) {
		reject(e)
	}
} 
const p = new Mypromise((resolve, reject) => {
	// throw new Error('executor错误')
	reject('失败了') 
})
p.then(res=> {
	console.log(res)
}, err=> {
	console.log(err + '---')
	return '111' // then失败回调return是一个字符串111
}).then(res => {
    console.log(res)
}, err => {
	console.log('捕获then')
	console.log(err)
})
// ***失败链式调用,then返回的是一个普通值
// 打印结果
//失败了---
//111

const p = new Mypromise((resolve, reject) => {
	// throw new Error('executor错误')
	reject('失败了') 
})
p.then(res=> {
	console.log(res)
}, err=> {
	console.log(err + '---')
	return new MyPromise((resolve, reject) => { // then返回的是一个promsie
			resolve('then返回的是一个promsie') 
	})
}).then(res => {
    console.log(res)
}, err => {
	console.log('捕获then')
	console.log(err)
})
// // ***失败链式调用,then返回的是一个promsie
// 打印结果 
//失败了---
//then返回的是一个promsie


// 捕获错误
p.then(res=> {
	console.log(res)
}, err=> {
	console.log(err + '---')
	throw new Error('错误了') // then中抛出一个错误
}).then(res => {
    console.log(res)
}, err => {
	console.log('捕获then')
	console.log(err) // 111
})
// 打印结果
//失败了---
//捕获then
//Error: 错误了

关于异步操作

此刻我们还没有处理异步的情况。如果then中的操作是异步操作呢

			this.succcessCallback.push(successFn(this.response) )
			this.failureCallback.push(failureFn(this.errmessage));

------------------
				// 这里以前我们是通过push的方法存储回调 如果then中的异步,我们就要push 一个函数了。
				
				this.succcessCallback.push(() => {
						// 如果是成功去执行成功的代码
						try {
							let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
							resolvePromise(res, resolve, reject) // 处理成功时结果
						} catch(e) {
							reject(e)
						}
					
				})
				this.failureCallback.push(() => {
						try {
							let res = failureFn(this.errmessage) // 执行成功的回调 参数是成功的响应数据
							resolvePromise(res, resolve, reject) // 处理成功时结果
						} catch(e) {
							reject(e)
						}
					
				});
	
	        }
------------------

// resolve
while (this.succcessCallback.length) {
            this.succcessCallback.shift()() // 此时这里就不用传递this.response
 }
// reject 同理
while (this.failureCallback.length) {
            this.failureCallback.shift()()
}

-------------------
p.then(res=> {
	// console.log(res)
	return new Mypromise((resolve, reject) => {
		setTimeout(() => {
			resolve('then中的异步操作')
		}, 1000)
	})
	
}, err=> {
	console.log(err + '---')
	throw new Error('错误了')
}).then(res => {
    console.log(res)
}, err => {
	console.log('捕获then')
	console.log(err) // 111
})

// 打印结果
// then中的异步操作  1s后打印

promise的循`环调用

下面的代码发生了promise的循环调用。当我们用p2接受p1.then执行结果, 恰巧在p1.then中我们也return了p2那么就发生了promise的循环调用这是不允许的

const p1 = new Promise((resolve, reject) => {
	resolve('1')
})
let p2 = p1.then(res= > {
	return p2
})
p2.then(res=> res, err=> console.log(err))


// TypeError: Chaining cycle detected for promise

也就是说在promise中, then方法中会自动判断回调函数执行结果是否和then返回的结果一样

successFn() 就是回调执行结果
pr 就是then方法中返回的那个promise对象,

const pr = new Mypromise((resolve, reject) => {
            // 当前状态成功了执行successFn 
	    	// 当前状态失败了执行 failureFn 
	    	if(this.status === FULFILLED) { 
            
	    		try {
					let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
					resolvePromise(res, resolve, reject) // 处理成功时结果
				} catch(e) {
					reject(e)
				}
	        } 
// then中返回的promise 和 执行回调的结果promise 不能是一个同一个promise
function resolvePromise(returnP, res, resolve, reject) {
	if (returnP === res) {
		// 如果回调的promise 和 then中要返回的promise是同一个对象那么就发生了 循环调用。 直接return 阻止代码继续向下执行
		return reject(new TypeError 'Chaining cycle detected for promise #<Promise>')
	}
    if (res instanceof Mypromise) {
        // 执行结果是一个promise
        // 调用then方法拿到执行结果如果成功执行resolve
        // 失败就调用reject
        res.then(value => resolve(value), err => reject(err))
    } else {
        // 普通值直接调用resolve方法
        resolve(res)
    }
}
// 在promise处理时要补一个实参就是pr
// 但是此时我们能拿到pr吗?
// 所以这里也需要加入异步操作
resolvePromise(pr, res, resolve, reject) // 处理成功时结果


const pr = new Mypromise((resolve, reject) => {
            // 当前状态成功了执行successFn 
	    	// 当前状态失败了执行 failureFn 
	    	if(this.status === FULFILLED) { 
            
	    		try {
					let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
					resolvePromise(res, resolve, reject) // 处理成功时结果
				} catch(e) {
					reject(e)
				}
	        } 

then的穿透

then中也可以不传递,如果没有传参可以进行then的调用

const p1 = new Promise((resolve, reject) => {
	resolve('1')
})
p1.then()
	.then()
	.then(val => console.log(val))


// 因此我可以进步补参的方法
then = (successFn, failureFn) => { 
	successFn ? successFn : value => value
}

目前位置已经实现了promise的大部分功能

全部代码

// 首先设置三种状态常量
const PENDDING = 'pendding';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class Mypromise {
	constructor(executor) {
		// new promise是会立即执行执行器,并且接受两个参数, resolve函数和reject函数
		// 执行器执行时发生了错误用try cathch去捕获执行器执行
		try {
			executor(this.resolve, this.reject)
			
		} catch (e) {
			// 如果有错误直接执行reject
			this.reject(e)
		}
		
	}
    status = PENDDING; 
    response = undefined // 定义一个成功的响应值
    errmessage = undefined // 失败的信息
    // 首先我们用属性succcessCallback存储successFn 和 failureCallback存储failureFn
    succcessCallback = [];
    failureCallback = [];
    // resolve是一个函数, 参数是响应值 
    resolve = (response) => { 
        if(this.status !== PENDDING ) return
    	this.status = FULFILLED; 
    	this.response = response; // 缓存成功消息
        // 当异步操作2s后执行到resolve的时候 如果succcessCallback 存在那么执行succcessCallback 
    	//this.succcessCallback && this.succcessCallback(this.response)
        while (this.succcessCallback.length) {
            this.succcessCallback.shift()()
        }
    } 
    // reject也是一个函数, 参数是错误消息 
    reject= (errmessage) => { 
        if(this.status !== PENDDING ) return
    	this.status = REJECTED; 
    	this.errmessage = errmessage // 缓存失败信息
        // 当异步操作2s后执行到reject的时候 如果failureCallback 存在那么执行failureCallback 
    	//this.failureCallback && this.failureCallback (this.errmessage )
        while (this.failureCallback.length) {
            this.failureCallback.shift()()
        }
    } 
    // then方法
    then = (successFn, failureFn) => { 
		successFn = successFn ? successFn : value => value // 补参如果有参数就用 如果没有参数就是一个函数
        // 获取到promise
        let pr = new Mypromise((resolve, reject) => {
                // 当前状态成功了执行successFn 
	    	// 当前状态失败了执行 failureFn 
	    	if(this.status === FULFILLED) {
				// 加入异步代码让它在pr生成后去用pr
				setTimeout(() => {
					// then中发生了错误 也用try catch捕获
					try {
						let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
						resolvePromise(pr, res, resolve, reject) // 处理成功时结果
					} catch(e) {
						reject(e)
					}
				}, 0)
		        
	    		
	        } else if (this.status === REJECTED) {
				setTimeout(() => {
					// then中发生了错误 也用try catch捕获
					try {
						let res = failureFn(this.errmessage) // 执行失败的回调 参数是失败的信息
						resolvePromise(pr, res, resolve, reject) // 处理失败时结果
					} catch(e) {
						reject(e)
					}
				}, 0)
			} else {
            // 如果是pendding状态我们首先储存succcessCallback  和 failureCallback 
    		// this.succcessCallback.push(successFn);
    		// this.failureCallback.push(failureFn);
			// 这里以前我们是通过push的方法存储回调 如果then中的异步,我们就要push 一个函数了。
				this.succcessCallback.push(() => {
						// 如果是成功去执行成功的代码
					setTimeout(() => {
						// then中发生了错误 也用try catch捕获
						try {
							let res = successFn(this.response) // 执行成功的回调 参数是成功的响应数据
							resolvePromise(pr, res, resolve, reject) // 处理成功时结果
						} catch(e) {
							reject(e)
						}
					}, 0)
					
				})
				this.failureCallback.push(() => {
					setTimeout(() => {
						// then中发生了错误 也用try catch捕获
						try {
							let res = failureFn(this.errmessage) // 执行失败的回调 参数是失败的信息
							resolvePromise(pr, res, resolve, reject) // 处理失败时结果
						} catch(e) {
							reject(e)
						}
					}, 0)
					
				});
	
	        }
	    })
    	
        return pr; // 返回一个promise
    }

}
// res是successFn执行的结果, resolve是成功的回调, reject是结束的回调

function resolvePromise(returnP, res, resolve, reject) {
	if (returnP === res) {
		// 如果回调的promise 和 then中要返回的promise是同一个对象那么就发生了 循环调用。 直接return 阻止代码继续向下执行
		return reject(new TypeError ('Chaining cycle detected for promise #<Promise>'))
	}
    if (res instanceof Mypromise) {
        // 执行结果是一个promise
        // 调用then方法拿到执行结果如果成功执行resolve
        // 失败就调用reject
        res.then(value => resolve(value), err => reject(err))
    } else {
        // 普通值直接调用resolve方法
        resolve(res)
    }
}
const p = new Mypromise((resolve, reject) => {
	// throw new Error('executor错误')
	resolve('成功')
})

let pp = p.then(
	res => {
		return pp
	}
)
pp.then(res=> res, err => {console.log(err)})
// 打印TypeError: Chaining cycle detected for promise #<Promise>