如何手写Promise
一、开始手写
1.Promise首先是个类,因为它可以实例化,我们使用es6提供的class来模拟它,当然构造函数也行,但是写的太烦了,算了算了
2.Promise接受一个立即执行的函数,这个函数又接受两个参数,一个resolve,一个reject
3.Promise包含三种状态,初始状态等待(pending),成功状态(fulFilled),失败状态(rejected)
4.Promise的状态由resolve和reject改变
// 用常量定义三个状态,避免单词写错的小尴尬事件 const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; class MyPromise { constructor (executor) { executor(this.resolve, this.reject) } status = PENDING resolve = () => { this.status = FULFILLED } reject = () => { this.status = REJECTED } }
5.Promise的状态一经改变,就不能再变化,状态已经锁定了
6.Promise的then方法接受两个函数作为参数,一个成功回调函数,一个失败回调函数,两个函数也接受一个参数,分别表示成功的结果和失败的原因,这两个值由resolve和reject分别传递过来
// 声明了两个参数value和reason来保存成功和失败的结果 // 方便then方法的成功与失败回调使用 // 而Promise的状态改变之后便锁定也很容易实现,在改变status之前判断status是否等于pending // 如果status !== pending,则代表status已经发生了改变,直接return class MyPromise { constructor (executor) { executor(this.resolve, this.reject) } status = PENDING // 成功的值 value = undefined // 失败的原因 reason = undefined resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason } then = (successCallback, failCallback) => { if (this.status === FULFILLED) { successCallback(this.value) } else { failCallback(this.reason) } } } let p = new MyPromise((res, rej) => { console.log(111); res("success") rej("fail") }) // 只会执行成功回调 p.then(value => { console.log(value); }, err => { console.log(err); })
7.Promise是用来处理异步的,那么我们试着加入异步的逻辑,怎么加?如果执行到then时,status === pending,将then里的方法储存起来,在resolve或reject的最后执行就行啦,类似于回调函数,类似于哈,但不是,你就这么想就行了
class MyPromise { constructor (executor) { executor(this.resolve, this.reject) } status = PENDING // 成功的值 value = undefined // 失败的原因 reason = undefined // 新增了成功回调 successCallback = undefined // 新增了失败回调 failCallback = undefined resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 执行异步逻辑 this.successCallback && this.successCallback(this.value) } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 执行异步逻辑 this.failCallback && this.failCallback(this.reason) } then = (successCallback, failCallback) => { if (this.status === FULFILLED) { successCallback(this.value) } else if (this.status === REJECTED) { failCallback(this.reason) } else { // 此时status为pending,说明处于异步情况,我们需要将成功回调和失败回调存储起来 // 在resolve()或reject()最后的地方执行 this.successCallback = successCallback this.failCallback = failCallback } } } // 首先then执行了,但是因为res在两秒后才会执行,所以then里的status===pending // 所以then里的回调不会执行,而是先储存起来 // 2秒后,res执行,成功回调在res的最后执行 // 这就是Promise通过then执行异步逻辑的原因,将同步代码保存起来,放在异步代码的最后执行 let p = new MyPromise((res, rej) => { setTimeout(() => { res("success") }, 2000) }) p.then(value => { console.log(value); }, err => { console.log(err); })
8.Promise的then方法可以多次调用,每次调用的方法可能都不相同,所以我们需要将所有的成功回调与失败回调保存起来全部保存起来,在status改变之后,遍历调用,当然同步代码不影响,因为直接执行了,异步代码因为是保存着的,所以现在我们要用一个数组给他包起来了
class MyPromise { constructor (executor) { executor(this.resolve, this.reject) } status = PENDING // 成功的值 value = undefined // 失败的原因 reason = undefined // 新增成功回调集合 successCallbackList = [] // 新增失败回调集合 failCallbackList = [] resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 执行异步逻辑 // this.successCallback && this.successCallback(this.value) // 数组的shift方法是将截取数组第一个,并返回截取的那个,会改变原数组,如果理解不了的可以for循环 while (this.successCallbackList.length) this.successCallbackList.shift()(this.value) } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 执行异步逻辑 // this.failCallback && this.failCallback(this.reason) while (this.failCallbackList.length) this.failCallbackList.shift()(this.value) } then = (successCallback, failCallback) => { if (this.status === FULFILLED) { successCallback(this.value) } else if (this.status === REJECTED) { failCallback(this.reason) } else { // 将回调函数保存在数组里,遍历执行 this.successCallbackList.push(successCallback) this.failCallbackList.push(failCallback) } } } let p = new MyPromise((res, rej) => { setTimeout(() => { res("success") }, 2000) }) p.then(value => { console.log(value); }, err => { console.log(err); }) p.then(value => { console.log(222); }, err => { console.log(err); }) p.then(value => { console.log(333); }, err => { console.log(err); })
9.Promise的链式调用,是解决回调地狱的关键。你知道为啥能链式调用吗?现在我们知道Promise对象可以执行.then,所以每次.then过后就返回一个新的Promise就行了。
10.下一个then的参数就是上一个Promise的返回,但是返回分两种,如果是普通值返回,直接返回就可以了,但是如果返回的是一个Promise对象,那么就得执行这个Promise,等待Promise的状态改变再返回这个Promsie的返回值了
class MyPromise { constructor (executor) { executor(this.resolve, this.reject) } status = PENDING // 成功的值 value = undefined // 失败的原因 reason = undefined // 成功回调集合 successCallbackList = [] // 失败回调集合 failCallbackList = [] resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 执行异步逻辑 // 数组的shift方法是将截取数组第一个,并返回截取的那个,会改变原数组,如果理解不了的可以for循环 while (this.successCallbackList.length) this.successCallbackList.shift()(this.value) } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 执行异步逻辑 while (this.failCallbackList.length) this.failCallbackList.shift()(this.reason) } then = (successCallback, failCallback) => { // 这里实例化了一个promise2,并且返回它,以供后面链式调用 let promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { let result = successCallback(this.value) // 判断result的类型 judgeResult(result, resolve, reject) } else if (this.status === REJECTED) { let result = failCallback(this.reason) // 判断result的类型 judgeResult(result, resolve, reject) } else { this.successCallbackList.push(successCallback) this.failCallbackList.push(failCallback) } }) return promise2 } } /** * 判断result,如果是个值,那就直接resolve * 如果是个Promise,那需要执行.then(),根据.then的结果返回 */ function judgeResult(result, resolve, reject) { if (result instanceof MyPromise) { result.then(value => resolve(value), error => reject(error)) } else { resolve(result) } } p.then(value => { console.log(value); return "p1--success" }).then(value => { console.log(value); return new MyPromise((res, rej) => { rej("p2--fail") }) }).then(undefined, err => { console.log(err); return new MyPromise((res, rej) => { setTimeout(()=> { res("p3--async") }, 1000) }) }).then(value => { console.log(value); })
11.then返回的如果是个Promise,那么这个返回不能是自己,否则就死循环了,这里也要处理一下
value = undefined // 失败的原因 reason = undefined // 成功回调集合 successCallbackList = [] // 失败回调集合 failCallbackList = [] resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 执行异步逻辑 // 数组的shift方法是将截取数组第一个,并返回截取的那个,会改变原数组,如果理解不了的可以for循环 while (this.successCallbackList.length) this.successCallbackList.shift()() } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 执行异步逻辑 while (this.failCallbackList.length) this.failCallbackList.shift()() } then = (successCallback, failCallback) => { // 这里实例化了一个promise2,并且返回它,以供后面链式调用 let promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { // 这里为了判断自己有没有返回自己,将所有处理都延时操作了,只有这样,才能取到promise2 setTimeout(() => { let result = successCallback(this.value) // 判断result的类型 judgeResult(promise2, result, resolve, reject) }, 0) } else if (this.status === REJECTED) { // 这里为了判断自己有没有返回自己,将所有处理都延时操作了,只有这样,才能取到promise2 setTimeout(() => { let result = failCallback(this.reason) // 判断result的类型 judgeResult(promise2, result, resolve, reject) }, 0) } else { this.successCallbackList.push(() => { // 这里为了判断自己有没有返回自己,将所有处理都延时操作了,只有这样,才能取到promise2 setTimeout(() => { let result = successCallback(this.value) // 判断result的类型 judgeResult(promise2, result, resolve, reject) }, 0) }) this.failCallbackList.push(() => { // 这里为了判断自己有没有返回自己,将所有处理都延时操作了,只有这样,才能取到promise2 setTimeout(() => { let result = failCallback(this.reason) // 判断result的类型 judgeResult(promise2, result, resolve, reject) }, 0) }) } }) return promise2 } } /** * 判断result,如果是个值,那就直接resolve * 如果是个Promise,那需要执行.then(),根据.then的结果返回 */ function judgeResult(promise2, result, resolve, reject) { if(promise2 === result) { return reject("别自己返回自己啊喂!!!") } if (result instanceof MyPromise) { result.then(value => resolve(value), error => reject(error)) } else { resolve(result) } } p.then(value => { console.log(value); return "p1--success" }).then(value => { console.log(value); return new MyPromise((res, rej) => { rej("p2--fail") }) }).then(undefined, err => { console.log(err); return new MyPromise((res, rej) => { setTimeout(()=> { res("p3--async") }, 1000) }) }).then(value => { console.log(value); }) // 测试不能返回自己 let p2 = p.then((value) => { console.log(value); return p2 }) p2.then((value)=> { console.log(value) }, (error) => { console.log(error) })
12.then方法的参数不是一个函数时,会自动转换成一个value => value的方法
13.Promise中,每个地方产生的错,最终都会被捕获,所以现在把该捕获错误的地方都捕一捕
// 如何捕获失败,用try/catch,在哪里捕获?在你认为会错的地方😀 class MyPromise { constructor (executor) { try { executor(this.resolve, this.reject) } catch (error) { this.reject(error) } } status = PENDING // 成功的值 value = undefined // 失败的原因 reason = undefined // 成功回调集合 successCallbackList = [] // 失败回调集合 failCallbackList = [] resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 执行异步逻辑 // 数组的shift方法是将截取数组第一个,并返回截取的那个,会改变原数组,如果理解不了的可以for循环 while (this.successCallbackList.length) this.successCallbackList.shift()() } reject = reason => { if (this.status !== PENDING) return this.status = REJECTED this.reason = reason // 执行异步逻辑 while (this.failCallbackList.length) this.failCallbackList.shift()() } then = (successCallback, failCallback) => { // 判断successCallback和failCallback是不是函数,如果不是,转化成一个函数 successCallback = typeof successCallback === 'function' ? successCallback : value => value failCallback = typeof failCallback === 'function' ? failCallback : value => value let promise2 = new MyPromise((resolve, reject) => { if (this.status === FULFILLED) { try { setTimeout(() => { let result = successCallback(this.value) // 判断result的类型 judgeResult(result, resolve, reject) }, 0) } catch (error) { reject(error) } } else if (this.status === REJECTED) { try { setTimeout(() => { let result = failCallback(this.reason) // 判断result的类型 judgeResult(result, resolve, reject) }, 0) } catch (error) { reject(error) } } else { this.successCallbackList.push(() => { try { setTimeout(() => { let result = successCallback(this.value) // 判断result的类型 judgeResult(result, resolve, reject) }, 0) } catch (error) { reject(error) } }) this.failCallbackList.push(() => { try { setTimeout(() => { let result = failCallback(this.reason) // 判断result的类型 judgeResult(result, resolve, reject) }, 0) } catch (error) { reject(error) } }) } }) return promise2 } } /** * 判断result,如果是个值,那就直接resolve * 如果是个Promise,那需要执行.then(),根据.then的结果返回 */ function judgeResult(promise2, result, resolve, reject) { throw(new Error("judgeResult error")) if(promise2 === result) { return reject("别自己返回自己啊喂!!!") } if (result instanceof MyPromise) { result.then(value => resolve(value), error => reject(error)) } else { resolve(result) } } let p = new MyPromise((res, rej) => { res("success") }) p.then().then("b").then(value => console.log(value))
14.Promise包含几个重要的静态方法,all、race、resolve、catch、finally,现在我们实现一下
// Promise.all 故名思意,就是等全部,全部成功之后,返回一个Promise对象,成功回调全部返回的数组 // 如果有失败,则会返回一个Promise对象,失败回调第一个失败的返回 static all(array) { try { if (!(array instanceof Array)) throw(new Error("请传入一个数组好吗")) if (array.length == 0) throw(new Error("请不要传入一个空数组好吗")) let arr = [] return new MyPromise((res, rej) => { // 这里有个坑,遍历的里面是异步,所以在最后res(arr)会有问题 // 所以每次arr.push之后,就比较一下两个arr的长度,长度相等说明已经遍历完了,可以执行res(arr)了 for(let item of array) { if (item instanceof MyPromise) { item.then(value=> { arr.push(value) }, error => { rej(error) }) } else { arr.push(item) if(arr.length == array.length) res(arr) } } // return res(arr) }) } catch (error) { console.log(error); } } // Promise.race故名思意,竞争,看谁先结束,返回的Promise就用谁的返回 static race(array) { try { if (!(array instanceof Array)) throw(new Error("请传入一个数组好吗")) if (array.length == 0) throw(new Error("请不要传入一个空数组好吗")) return new MyPromise((res, rej) => { for(let item of array) { if (item instanceof MyPromise) { item.then(value=> { res(value) }, error => { rej(error) }) } else { res(item) } } }) } catch (error) { console.log(error); } } // Promise.resolve 参数如果不是Promise对象,直接返回一个成功回调为参数的Promise对象 // 如果参数是Promise对象,那直接返回参数即可 static resolve(params) { try { if(params instanceof MyPromise) { return params } else { return new MyPromise(res => { res(params) }) } } catch (error) { console.log(error); } } // Promise.catch就是一个没有成功回调的then static catch(failCallback) { return this.then(null, failCallback) } // Promise.finally // 不管成功还是失败都要执行 // 回调函数中不包含当前Promise的返回值,也就是callback不传递参数 // 但是会把当前Promise的返回值return出去,所以调用this.then,获取当前Promise的返回值 // 如果finally返回一个Promise,会执行,但是仍然返回当前Promise的返回值,而不是fianlly返回的Promise的返回值 static finally(callback) { this.then(value => { this.resolve(callback()).then(() => value) }, error => { this.resolve(callback()).then(() => { throw error }) }) } p1.then(()=> { return "1111" }).finally(value => { console.log(value); // undefined return new MyPromise(res => { res("2222") }) }).then(value=> { console.log(value); // "1111" })