什么是Promise和Promise A+
Promise是一种异步编程解决方案,比传统的解决方案(回调函数和事件),更合理和更强大。主要解决了回调地狱的问题。它最早是由社区提出和实现,后来ES6 将其统一规范( Promise A+规范 ),写进了标准语法,提供了原生的Promise对象。
Promise的特点:
1、Promise有三种状态(pending(进行中),fulfilled(成功),rejected(失败))。三种状态只能由pending变为fulfilled或者rejected,而且状态一旦发生改变就会凝固,无法再次改变。而且Promise的状态是不受外部影响的, 只有异步操作的结果,才可以决定当前是哪一种状态,任何其他操作都无法改变这个状态 。
2、可以使用then、catch、finally原型方法,链式调用依次执行异步操作,还有一系列静态方法如:resolve,reject,all, race ,any,allSettled。
接下来手写实现符合Promise A+的Promise对象。第四步的then边界处理有点麻烦有时间可以深入研究。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class myPromise1{ constructor(executor){ //初始化状态 this.state = 'pending' //三种状态pending,fulfilled, rejected //初始化值 this.value = undefined //初始化失败的原因 this.reason = undefined //初始化成功的 let resolve = (value) =>{ if(this.state === 'pending'){//只有当pending的时候状态才会改变;promise的凝固状态 this.state = 'fulfilled'//改变状态值为成功 this.value = value console.log(`状态:${this.state};值:${this.value}`); } } let reject = (reason) =>{ if(this.state === 'pending'){ this.state = 'rejected' this.reason = reason console.log(`状态:${this.state};值:${this.reason}`); } } //执行器如果有报错直接就执行reject方法 try { executor(resolve,reject)//立即执行执行器方法并传递参数;(executor指向的是nwe myPromise(fn)里传的那个函数fn) } catch (error) { reject(error) } } } let p1 = new myPromise1((resolve,reject) => { resolve('p1成功') reject('p1失败') }) |
promise的then方法是promise的核心重要方法,为更加清晰的理解它的实现方式。我们分三步实现。1、实现基本的then方法调用,并支持异步调用。2、实现then方法的链式调用。3、为then方法添加更加丰富全面的边界处理。
1、实现then方法的基本调用,并支持异步调用。
前言:then方法是有两个回调参数的一个是onFulfilled成功回调,一个是 onRejected失败的回调。注意当前的失败回调onRejected,只能处理当前实例的失败(reject)的情况,而我们的catch可以捕获任何报错。
实现then的基本调用:我们的then方法是调用我们的myProimse类的公共方法,所以直接在类里面写方法就可以。 前面我们已经实现了内部resolve,reject方法,在我们调用resolve、reject方法的时候,会改变当前实例的value、reason和状态。所以在我们就可以在then方法里直接判断状态(fulfilled,rejected),然后执行对应的回调(onFulfilled、 onRejected ),再然后把当前实例的值(value或者reason)传进去就可以。
实现then方法的异步调用:promise的异步调用是隔段时间调用resolve、reject内部方法,如果我们把需要执行的代码(then方法里的回调函数onFulfilled、 onRejected)放在reslove、reject内部函数里面执行,那么我们在一段时间之后调用resolve、reject方法就可以调用到异步的代码,这样就可以实现异步效果了。又因为我们的then可能会执行好几个,所以我们需要执行的回调会有好几个,我们可以写在数组里onReslovedCallbacks(成功回调数组)、onRejectedCallbacks(失败回调数组),在执行resolve、reject方法时分别调用onReslovedCallbacks、 onRejectedCallbacks数组里的所有方法。对了在我们使用了异步方法之后,由于我们还没有执行resolve,reject方法,其实我们的promise的状态是pending的,所以我们需要判断状态是pending的时候再执行上面操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
class myPromise2{ constructor(executor){ //初始化状态 this.state = 'pending' //三种状态pending,fulfilled, rejected //初始化值 this.value = undefined //初始化失败的原因 this.reason = undefined //存放成功时需要执行的函数列表 this.onReslovedCallbacks = [] //存放失败时需要执行的函数列表 this.onRejectedCallbacks = [] //初始化成功的 let resolve = (value) =>{ if(this.state === 'pending'){//只有当pending的时候状态才会改变;promise的凝固状态 this.state = 'fulfilled'//改变状态值为成功 this.value = value console.log(`状态:${this.state};值:${this.value}`); this.onReslovedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } let reject = (reason) =>{ if(this.state === 'pending'){ this.state = 'rejected' this.reason = reason console.log(`状态:${this.state};值:${this.reason}`); this.onRejectedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } //执行器如果有报错直接就执行reject方法 try { executor(resolve,reject)//立即执行执行器方法并传递参数;(executor指向的是nwe myPromise(fn)里传的那个函数fn) } catch (error) { reject(error) } } then(onFulfilled,onRejected){ //立即执行resolve或者reject方法 if(this.state === 'fulfilled'){ onFulfilled(this.value) } if(this.state === 'rejected'){ onRejected(this.reason) } //异步执行resolve或者reject方法;将需要执行的方法放进一个数组;然后去resolve和reject方法里循环执行;这样就可以做到外面异步调用resolve、reject方法的时候异步执行该函数 if(this.state === 'pending'){ this.onReslovedCallbacks.push(() => {//用一个函数包起来,利用闭包将传参缓存起来 onFulfilled(this.value) }) this.onRejectedCallbacks.push(() => { onRejected(this.reason) }) } } } let p2 = new myPromise2((resolve,reject) => { // resolve('p2成功') setTimeout(() => { resolve('p2异步成功') }, 2000) }) p2.then(res => { console.log(res,21); }) p2.then(res => { console.log(res,22); }) p2.then(res => { console.log(res,23); }) |
2、实现then方法的链式调用。
promise想要链式调用,需要满足两个条件:1、then的返回值是一个promise。2、下一个then能拿到上一个then的返回值。所以我们需要改造一下then方法,让他返回一个promise,并把当前then需要返回的值传递出去作为第二个then的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
class myPromise3{ constructor(executor){ //初始化状态 this.state = 'pending' //三种状态pending,fulfilled, rejected //初始化值 this.value = undefined //初始化失败的原因 this.reason = undefined //存放成功时需要执行的函数列表 this.onReslovedCallbacks = [] //存放失败时需要执行的函数列表 this.onRejectedCallbacks = [] //初始化成功的 let resolve = (value) =>{ if(this.state === 'pending'){//只有当pending的时候状态才会改变;promise的凝固状态 this.state = 'fulfilled'//改变状态值为成功 this.value = value console.log(`状态:${this.state};值:${this.value}`); this.onReslovedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } let reject = (reason) =>{ if(this.state === 'pending'){ this.state = 'rejected' this.reason = reason console.log(`状态:${this.state};值:${this.reason}`); this.onRejectedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } //执行器如果有报错直接就执行reject方法 try { executor(resolve,reject)//立即执行执行器方法并传递参数;(executor指向的是nwe myPromise(fn)里传的那个函数fn) } catch (error) { reject(error) } } then(onFulfilled,onRejected){ let afterMyPromise3 = new myPromise3((resolve,reject) => {//实例化一个myPromise3对象作为返回的promise //立即执行resolve或者reject方法 if(this.state === 'fulfilled'){ let beforePromiseReslove = onFulfilled(this.value) //这里拿到的是前面then的返回值 resolve(beforePromiseReslove)//调用resolve方法,将前面then的返回结果传递给后面的then } if(this.state === 'rejected'){ let beforePromiseReject = onRejected(this.reason) reject(beforePromiseReject) } //异步执行resolve或者reject方法;将需要执行的方法放进一个数组;然后去resolve和reject方法里循环执行;这样就可以做到外面异步调用resolve、reject方法的时候异步执行该函数 if(this.state === 'pending'){ this.onReslovedCallbacks.push(() => {//用一个函数包起来,利用闭包将传参缓存起来 let beforePromiseReslove = onFulfilled(this.value) resolve(beforePromiseReslove) }) this.onRejectedCallbacks.push(() => { let beforePromiseReject = onRejected(this.reason) reject(beforePromiseReject) }) } }) return afterMyPromise3//返回一个promise } } let p3 = new myPromise3((resolve,reject) => { // resolve('p2成功') setTimeout(() => { resolve('p3异步成功') }, 3000) }) p3.then(res => { console.log(res,'第一个then'); return 3 }).then(res => { console.log(res,'第二个then'); }) |
3、为then方法添加更加丰富全面的边界处理。
通过上面的步骤我们基本上就实现了promise最核心的then方法。但是还有一些边界值的处理需要我们完善一下。因为边界处理会有很多种情况,我们需要将他们放在一个独立函数中;在我们使用边界处理函数的时候,需要传入当前实例,又因为调用的方法就在当前实例里面,所以我们需要用到setTimeout将其转为异步,这样就可以拿到当前实例和返回值进行比较了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
function resolvePromise(afterPromise,beforePromiseResult,resolve,reject){ //1.处理循环调用 if(afterPromise === beforePromiseResult) { let errorMeg = new TypeError('Uncaught (in promise) TypeError: Chaining cycle detected for promise [object Promise]') // console.error(errorMeg) reject(errorMeg) } let called = false;// 标记是否已调用,防止多次调用 // 2. 如果 beforePromiseResult 是 myPromise4 实例 if (beforePromiseResult instanceof myPromise4) { // 根据 beforePromiseResult 的状态调用 resolve 或 reject beforePromiseResult.then( y => { resolvePromise(afterPromise,y,resolve,reject); }, reason => { reject(reason); } ); } else if (beforePromiseResult !== null && (typeof beforePromiseResult === 'object' || typeof beforePromiseResult === 'function')) { // 3. 如果 beforePromiseResult 是对象或函数 try { // 获取 beforePromiseResult 的 then 方法 const then = beforePromiseResult.then; if (typeof then === 'function') { // 如果 then 是函数 // 使用 beforePromiseResult 作为上下文调用 then 方法 then.call( beforePromiseResult, y => { // 成功回调 if (called) return; // 如果已经调用过,直接返回 called = true; // 递归处理 y resolvePromise(afterPromise, y, resolve, reject); }, reason => { // 失败回调 if (called) return; // 如果已经调用过,直接返回 called = true; reject(reason); } ); } else { // 如果 then 不是函数 // 直接调用 resolve resolve(beforePromiseResult); } } catch (error) { // 如果获取或调用 then 方法抛出异常 if (called) return; // 如果已经调用过,直接返回 called = true; reject(error); } } else { // 4. 如果 beforePromiseResult 不是对象或函数 // 直接调用 resolve resolve(beforePromiseResult); } } class myPromise4{ constructor(executor){ //初始化状态 this.state = 'pending' //三种状态pending,fulfilled, rejected //初始化值 this.value = undefined //初始化失败的原因 this.reason = undefined //存放成功时需要执行的函数列表 this.onReslovedCallbacks = [] //存放失败时需要执行的函数列表 this.onRejectedCallbacks = [] //初始化成功的 let resolve = (value) =>{ if(this.state === 'pending'){//只有当pending的时候状态才会改变;promise的凝固状态 this.state = 'fulfilled'//改变状态值为成功 this.value = value console.log(`状态:${this.state};值:${this.value}`); this.onReslovedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } let reject = (reason) =>{ if(this.state === 'pending'){ this.state = 'rejected' this.reason = reason console.log(`状态:${this.state};值:${this.reason}`); this.onRejectedCallbacks.forEach(fn => fn())//循环执行成功的回调函数 } } //执行器如果有报错直接就执行reject方法 try { executor(resolve,reject)//立即执行执行器方法并传递参数;(executor指向的是nwe myPromise(fn)里传的那个函数fn) } catch (error) { reject(error) } } then(onFulfilled,onRejected){ // 如果不传处理函数,则使用默认处理函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };//如果不传onRejected就直接抛错 let afterMyPromise4 = new myPromise4((resolve,reject) => { //立即执行resolve或者reject方法 if(this.state === 'fulfilled'){ setTimeout(() => {//保证异步主要是为了在这里能拿到afterMyPromise4与返回值比较进行抛错处理 try{//这里的try catch主要是为了捕获不传onRejected时的抛错 let beforePromiseReslove = onFulfilled(this.value) //这里拿到的是前面then的返回值 resolvePromise(afterMyPromise4,beforePromiseReslove,resolve,reject) //调用边界处理函数 }catch(err){// reject(err); } }) } if(this.state === 'rejected'){ setTimeout(() => { try{ let beforePromiseReject = onRejected(this.reason) resolvePromise(afterMyPromise4,beforePromiseReject,resolve,reject) }catch(err){ reject(err); } }) } //异步执行resolve或者reject方法;将需要执行的方法放进一个数组;然后去resolve和reject方法里循环执行;这样就可以做到外面异步调用resolve、reject方法的时候异步执行该函数 if(this.state === 'pending'){ this.onReslovedCallbacks.push(() => {//用一个函数包起来,利用闭包将传参缓存起来 setTimeout(() => {//保证异步主要是为了在这里能拿到afterMyPromise4与返回值比较进行抛错处理 try{ let beforePromiseReslove = onFulfilled(this.value) //这里拿到的是前面then的返回值 resolvePromise(afterMyPromise4,beforePromiseReslove,resolve,reject) }catch(err){ reject(err); } }) }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try{ let beforePromiseReject = onRejected(this.reason) resolvePromise(afterMyPromise4,beforePromiseReject,resolve,reject) }catch(err){ reject(err); } }) }) } }) return afterMyPromise4 } } let p4 = new myPromise4((resolve,reject) => { resolve(4) }) let p4_1 = p4.then(res => p4_1) p4_1.then(res => { console.log(res,'循环调用'); }, reject => { console.log(reject,'循环调用报错'); }) |
catch方法和then方法一样直接写在myPromise里面。其实then方法很简单,就是then的一个语法糖, 就是调用then方法仅传一个失败处理函数 。finally与catch一样也是then的一个语法糖,不管myPromise是成功或者失败都会执行该回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class myPromise{ //...这里省略之前重复代码 catch(onRejected) { return this.then(null, onRejected); } finally(callback) { return this.then(// 调用then方法,传入两个相同的处理函数 value => { // 创建一个新的Promise实例,确保异步执行callback return myPromise5.resolve(callback()).then(() => value); }, reason => { // 创建一个新的Promise实例,确保异步执行callback return myPromise5.resolve(callback()).then(() => { throw reason; }); } ); } } |
Promise的静态方法就是可以用Promise直接调用的方法。我们前面写的resolve、reject方法是Promise的内部方法,他们只能在Promise内部使用。我们在写静态方法的时候直接调用内部方法就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class myPromise{ //...这里省略之前重复代码 // 静态resolve方法 static resolve(value) { if (value instanceof myPromise5) { return value; } return new myPromise5((resolve, reject) => { resolve(value); }); } // 静态reject方法 static reject(reason) { return new myPromise5((resolve, reject) => { reject(reason); }); } } |
Promise的race、all方法是Promise处理多个Promise的方法。Promise. race:只要有一个Promise的状态发生改变,当前就Promise状态就随之发生改变,可以理解为一个竞速原则。Promise.all:当所有Promise状态都改变了,当前Promise状态才发生改变(所有Promise都返回成功才成功,否则就是失败)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
static race(promises){ return new myPromise5((resolve, reject) => { promises.forEach(promiseItem => { promiseItem.then(resolve, reject); }) }) } static all(promises){ return new myPromise5((resolve, reject) => { let resultArry = [] let currentIndex = 0 promises.forEach((item, index) => { myPromise5.resolve(item).then(res => { resultArry[index] = res currentIndex += 1 if(resultArry.length === currentIndex) resolve(resultArry) },reject) }) }) } |
Promise的allSettled方法和any方法跟all、race方法很像。 Promise. allSettled:当所有的promise完成时(不管成功或者失败),当前promise实例就会成功(注意他没有失败catch);需要对所有promise进行遍历,每一个promise都完成时,将每个对象的结果作为一个对象传递出去。Promise.any:当有一个promise成功时当前promise实例就显示成功的结果,所有promise失败时就显示失败的结果数组,也需要遍历所有promise拿到结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
static allSettled(promises) { let resultArry = [] let currentIndex = 0 return new myPromise5((resolve,reject) => { promises.forEach((item,index) => { myPromise5.resolve(item).then(res => { resultArry[index] = {status: "fulfilled", value: res} currentIndex += 1 if(currentIndex === promises.length) resolve(resultArry) },(err) => { resultArry[index] = {status: "rejected", reason: err} currentIndex += 1 if(currentIndex === promises.length) resolve(resultArry)}) }) }) } static any(promises) { let errorArry = [] let currentIndex = 0 return new myPromise5((resolve, reject) => { promises.forEach((item, index) => { myPromise5.resolve(item).then(resolve,err => { errorArry[index] = err currentIndex += 1 if(currentIndex === promises.length) reject(new AggregateError(errorArry,'All promises were rejected')) }) }) }) } |