Promise核心 本章来自己开发一个Promise实现,提升异步编程的能力。
起步构建 首先声明定义类并声明Promise状态与值,有以下几个细节需要注意。
executor为执行者
当执行者出现异常时触发拒绝 状态,并异步抛出错误
如果直接抛出错误,在打印时,看不到PROMISE本身的状态与值,所以将抛出错误放在异步宏任务中
这个结果和原生Promsie是相同的
使用静态属性保存状态值
状态只能改变一次,所以在resolve与reject添加条件判断
因为 resolve
或rejected
方法在executor中调用,作用域也是executor作用域,这会造成在外部确认状态时:this指向window,现在我们使用的是class定义,this为undefined。所以我们要改变this指向
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 class PROMISE { static PENDING = "pending"; static FULFILLED = "fulfilled"; static REJECTED = "rejected"; constructor(executor) { this.value = undefined; this.status = PROMISE.PENDING; try { executor(this.resolve.bind(this), this.reject.bind(this)); } catch (err) { this.reject(err); setTimeout(() => { throw err; }); } } resolve(result) { if (this.status === PROMISE.PENDING) { this.status = PROMISE.FULFILLED; this.value = result; } } reject(reason) { if (this.status === PROMISE.PENDING) { this.status = PROMISE.REJECTED; this.value = reason; } } }
测试状态改变
1 2 3 4 5 6 7 console.log(new PROMISE(() => {})); console.log( new PROMISE((resolve, reject) => { resolve("fulfilled"); reject("rejected"); }) );
测试executor执行异常
1 2 3 4 5 6 7 8 9 10 11 console.log( new PROMISE((resolve, reject) => { console.log(as); }) ); //对比原生Promise console.log( new Promise((resolve, reject) => { console.log(as); }) );
THEN 现在添加then方法来处理状态的改变,有以下几点说明
then可以有两个参数,即成功和错误时的回调函数
若then处理的Promise状态为fulfilled,then的函数参数都不是必须的,如果传入非函数则将被忽略,还需要设置默认值为函数,用于处理没传参,或传入非函数的情况,并将PROMISE的value返回,为后期链式调用then传递值。
若then所处理的Promise状态为rejected且没有使用then的onRejected处理时,将会报错。
当执行then传递的函数发生异常时,统一交给onRejected来处理错误
then的执行是异步任务
基础构建 先观察原生Promsie.then的特点
1 2 3 4 5 6 7 8 9 10 11 new Promise((res, rej) => { console.log("Ashun"); setTimeout(() => { console.log("ashun"); res("as"); }); }).then( (result) => console.log(result), (reason) => console.log(reason) ); console.log("阿顺特烦恼");
若Promise状态为rejected,且没有被then中的onRejected函数处理,将会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 new Promise((res, rej) => { rej("Ashun"); }); //没有传递onRejeted new Promise((res, rej) => { rej("Ashun"); }).then((result) => console.log(result)); //onRejeted不是函数,证明没有处理异常,依旧会报错 new Promise((res, rej) => { rej("Ashun"); }).then((result) => console.log(result), "rejected");
即便是空函数,也代表对异常做了处理,便不会报错
1 2 3 4 5 6 new Promise((res, rej) => { rej("Ashun"); }).then( (result) => console.log(result), () => {} );
实现基本功能
对onResolve设置默认函数,并返回this.value,当PROMISE状态为fulfilled时,不传参,也会将值传递给下一个then
rejected处理
实现方法(一)
不为onRejected设置默认函数,若设置了默认函数,则会默认处理rejected状态
设置一个变量isfilter
,监听rejected是否被处理
由于不确定onRejected是否为函数,也没有为其设置默认函数,所以在后期执行时,要判断其类型,再设置isfilter
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 class PROMISE { … constructor(executor) { …… this.isfilter = false; } …… then(onResolve, onReject) { if (!(onResolve instanceof Function)) { onResolve = () => this.value; } if (this.status === PROMISE.FULFILLED) { try { onResolve(this.value); } catch (err) { onReject(err); } } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { try { onReject(this.value); } catch (err) { onReject(err); } this.isfilter = true; } else { throw new Error("PROMISE status rejected"); } } } }
这种方式有一个弊端,就是一旦抛出错误,后续的同步代码将不再被执行
1 2 3 4 new PROMISE((res, rej) => { rej("Ashun"); }).then(); console.log("阿顺特烦恼"); //rejected没有被处理,抛出错误,后续同步代码不会执行
实现方法(二)
在reject回调函数中,异步判断isfilter
,因为外部代码自上而下执行,isfilter的初始值为false,若不异步判断,则无论是否被处理,一旦执行reject回调函数,就会报错。
1 2 3 4 5 6 7 8 //代码自上而下执行,若在reject回调函数中判断isfilter,则要异步判断,等待then处理后,再判断 new PROMISE((res, rej) => { rej("Ashun"); }).then( (result) => console.log(result), (reason) => console.log(reason) );
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 reject(reason) { if (this.status === PROMISE.PENDING) { this.status = PROMISE.REJECTED; this.value = reason; // 异步判断是否被过滤,等待then执行完毕,判断rejected是否被处理; setTimeout(() => { if (!this.isfilter) { throw new Error("PROMISE status rejected"); } }); } } then(onResolve, onReject) { if (!(onResolve instanceof Function)) { onResolve = () => this.value; } if (this.status === PROMISE.FULFILLED) { try { onResolve(this.value); } catch (err) { onReject(err); } } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { try { onReject(this.value); } catch (err) { onReject(err); } this.isfilter = true; } } }
下面来测试then方法,结果正常输出Ashun
1 2 3 4 5 6 7 new PROMISE((res, rej) => { rej("Ashun"); }).then( (result) => console.log(result), (reason) => console.log(reason) ); console.log("阿顺特烦恼");
若没有处理rejected,会报错,并且不会影响后续同步代码的执行
1 2 3 4 new PROMISE((res, rej) => { rej("Ashun"); }).then(result => console.log(result)); console.log("阿顺特烦恼");
异步任务 但上面的代码并不是异步执行的,使用setTimeout来将onFulfilled与onRejected做为异步宏任务执行
isfilter
的改变不使用setTimeout包裹,只要onReject为Function,就立即设置isfilter=true
,这样才能够让resolve及时监听isfilter
的改变
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 then(onResolve, onReject) { if (!(onResolve instanceof Function)) { onResolve = () => this.value; } if (this.status === PROMISE.FULFILLED) { setTimeout(() => { try { onResolve(this.value); } catch (err) { onReject(err); } }); } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { setTimeout(() => { try { onReject(this.value); } catch (err) { onReject(err); } }); this.isfilter = true; } else { throw new Error("PROMISE status rejected"); } } }
现在再执行代码,已经有异步效果了,先输出了阿顺特烦恼
1 2 3 4 5 6 7 8 new PROMISE((res, rej) => { // res("Ashun"); rej("Ashun"); }).then( (result) => console.log(result), (reason) => console.log(reason) ); console.log("阿顺特烦恼");
PENDING异步 当在PROMISE中异步确认状态
时,then处理的是pending状态的PROMISE,所以不会执行对应的处理函数
1 2 3 4 5 6 7 8 9 new PROMISE((res, rej) => { setTimeout(() => { res("as"); }); }).then( (result) => console.log(result), (reason) => console.log(reason) ); # 由于处理的是pending状态的PROMISE,所以不会执行处理函数
为了处理以上情况,需要进行几点改动
在构造函数中添加callbacks来保存pending状态时处理函数,当状态改变时,即resolve/reject
被调用时,再在resolve/reject
函数体中调用callbacks对应状态的处理函数
callbacks中的处理函数也要设置为异步调用
只有在异步确认状态时,才会向callbacks中压入对应处理函数,所以在调用时,要判断处理函数是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 constructor(executor){ …… this.callbacks={}; } resolve(result) { if (this.status === PROMISE.PENDING) { this.status = PROMISE.FULFILLED; this.value = result; this.callbacks.onResolve && this.callbacks.onResolve(this.value); } } reject(reason) { if (this.status === PROMISE.PENDING) { this.status = PROMISE.REJECTED; this.value = reason; this.callbacks.onReject && this.callbacks.onReject(this.value); setTimeout(() => { if (!this.isfilter) throw new Error("PROMISE status rejected"); }); } }
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 then(onResolve, onReject) { //设置默认值 if (!(onResolve instanceof Function)) { onResolve = () => this.value; } //同步确认状态处理,直接执行对应处理函数 if (this.status === PROMISE.FULFILLED) { setTimeout(() => { try { onResolve(this.value); } catch (err) { onReject(err); } }); } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { onReject(this.value); } catch (err) { onReject(err); } }); } } //异步确认状态处理 //先将处理函数添加到callbacks中,当状态发生改变时,再在this.resolve/reject中调用 if (this.status === PROMISE.PENDING) { this.callbacks.onResolve = (result) => { setTimeout(() => { try { onResolve(result); } catch (err) { onReject(err); } }); }; this.callbacks.onReject = (reason) => { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { onReject(reason); } catch (err) { onReject(err); } }); } }; } }
链式操作 Promise中的then是链式调用执行的,所以then也要默认返回状态为fulfilled的Promise。
then的onReject函数是对前面Promise的rejected的处理
但默认返回的Promise状态要为fulfilled,所以在调用onRejected后,需要改变当前promise为fulfilled状态,并把执行结果传入。让下一个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 then(onResolve, onReject) { //设置默认值 if (!(onResolve instanceof Function)) { onResolve = () => this.value; } //默认返回一个PROMISE return new PROMISE((resolve, reject) => { //同步确认状态处理 if (this.status === PROMISE.FULFILLED) { setTimeout(() => { try { let preResult = onResolve(this.value); resolve(preResult); } catch (err) { reject(err); } }); } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { let preReason = onReject(this.value); resolve(preReason); } catch (err) { reject(err); } }); } } //异步确认状态处理 if (this.status === PROMISE.PENDING) { this.callbacks.onResolve = (result) => { setTimeout(() => { try { let preResult = onResolve(result); resolve(preResult); } catch (err) { reject(err); } }); }; this.callbacks.onReject = (reason) => { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { let preReason = onReject(reason); resolve(preReason); } catch (err) { reject(err); } }); } }; } }); }
下面经过测试后,链式操作已经有效了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 new PROMISE((resolve, reject) => { console.log("Ashun"); setTimeout(() => { console.log("ashun"); reject("as"); }); }) .then( (result) => result, (reason) => reason ) .then() .then( (result) => console.log(result), (reason) => console.log(reason) ); console.log("阿顺特烦恼");
返回类型 原生Promise.then,若在then中手动返回一个新的Promise并确认状态,这个手动返回的Promise能够改变当前then的状态,并且下一个then就是对返回的Promise的处理。
1 2 3 4 5 6 7 8 new Promise((resolve, reject) => { resolve("Promise status: fulfilled"); }) .then((result) => { console.log(result); return Promise.reject("then status: rejected"); }) .then(null, (err) => console.log(err));
基本实现 我们若要实现这个效果,就要判断then返回结果的类型是否为PROMISE,若是PROMISE,我们直接调用 preRusult.then(resolve,reject)
即可,因为调用then会等待手动返回的PROMISE确认状态后执行。
让手动返回的PROMISE状态改变当前then默认返回的PROMISE的状态
使用then处理,若手动返回PROMISE确认状态为fulfilled,就执行默认返回PROMISE的resolve,让其状态也变为fulfilled
同理,若手动返回PROMISE确认状态为rejected,就执行默认返回PROMISE的reject,让其状态也变为rejected
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 then(onResolve, onReject) { //设置默认值 ……… return new PROMISE((resolve, reject) => { //同步确认状态处理 if (this.status === PROMISE.FULFILLED) { setTimeout(() => { try { let preResult = onResolve(this.value); if (preResult instanceof PROMISE) { preResult.then(resolve, reject); } else { resolve(preResult); } } catch (err) { reject(err); } }); } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { let preReason = onReject(this.value); if (preReason instanceof PROMISE) { preReason.then(resolve, reject); } else { resolve(preReason); } } catch (err) { reject(err); } }); } } //异步确认状态处理 if (this.status === PROMISE.PENDING) { this.callbacks.onResolve = (result) => { setTimeout(() => { try { let preResult = onResolve(result); if (preResult instanceof PROMISE) { preResult.then(resolve, reject); } else { resolve(preResult); } } catch (err) { reject(err); } }); }; this.callbacks.onReject = (reason) => { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { try { let preReason = onReject(reason); if (preReason instanceof PROMISE) { preReason.then(resolve, reject); } else { resolve(preReason); } } catch (err) { reject(err); } }); } }; } }); }
测试能够到的正确结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 new PROMISE((res, rej) => { console.log("Ashun"); setTimeout(() => { console.log("ashun"); rej("as"); }); }) .then( (result) => result, (reason) => reason ) .then((result) => { console.log(result); return new PROMISE((res, rej) => { res("then2 status Fulfilled"); }); }) .then( (result) => console.log(`resolve__:${result}`), (reason) => console.log(`rejected__:${reason}`) ); console.log("阿顺特烦恼");
代码复用 现在发现pendding、fulfilled、rejected 状态的代码非常相似,所以可以提取出方法Parse来复用
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 then(onResolve, onReject) { //设置默认值 …… return new PROMISE((resolve, reject) => { //同步确认状态处理 if (this.status === PROMISE.FULFILLED) { setTimeout(() => { this.Parse(onResolve(this.value), resolve, reject); }); } if (this.status === PROMISE.REJECTED) { setTimeout(() => { this.Parse(onReject(this.value), resolve, reject); }); } //异步确认状态处理 if (this.status === PROMISE.PENDING) { this.callbacks.onResolve = (result) => { setTimeout(() => { this.Parse(onResolve(result), resolve, reject); }); }; this.callbacks.onReject = (reason) => { setTimeout(() => { this.Parse(onReject(reason), resolve, reject); }); }; } }); } Parse(Operation, resolve, reject) { try { let preReason = Operation; if (preReason instanceof PROMISE) { preReason.then(resolve, reject); } else { resolve(preReason); } } catch (err) { reject(err); } }
返回约束 then手动返回的promise不能是then默认返回Promise,会产生循环调用,下面是原生Promise的示例将产生错误
1 2 3 let promise = new Promise((res, rej) => { res("fulfilled"); }).then((result) => promise);
解决上面的问题来完善代码,添加当前promise做为parse的第一个参数与函数执行结果进行比对
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 then(onResolve, onReject) { //设置默认值 …… let promise = new PROMISE((resolve, reject) => { //同步确认状态处理 if (this.status === PROMISE.FULFILLED) { setTimeout(() => { this.Parse(promise, onResolve(this.value), resolve, reject); }); } if (this.status === PROMISE.REJECTED) { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { this.Parse(onReject(this.value), resolve, reject); }); } } //异步确认状态处理 if (this.status === PROMISE.PENDING) { this.callbacks.onResolve = (result) => { setTimeout(() => { this.Parse(promise, onResolve(result), resolve, reject); }); }; this.callbacks.onReject = (reason) => { if (onReject instanceof Function) { this.isfilter = true; setTimeout(() => { this.Parse(onReject(reason), resolve, reject); }); } }; } }); return promise; } Parse(promise, Operation, resolve, reject) { if (promise === Operation) { throw new Error("Chaining cycle detected for promise"); } try { let preReason = Operation; if (preReason instanceof PROMISE) { preReason.then(resolve, reject); } else { resolve(preReason); } } catch (err) { reject(err); } }
现在进行测试也可以得到原生一样效果了
1 2 3 let promise = new PROMISE((res, rej) => { res("fulfilled"); }).then((result) => promise);
静态方法 RESOLVE 下面来实现原生Promise的静态方法Promise.resolve
用于快速返回一个状态为resolve的Promise
1 2 3 Promise.resolve( new Promise((res) => res("ashuntefannao")) ).then((result) => console.log(result));
创建静态方法static resolve
1 2 3 4 5 6 7 8 9 static resolve(value) { return new PROMISE((resolve, reject) => { if (value instanceof PROMISE) { value.then(resolve, reject); } else { resolve(value); } }); }
测试
1 2 3 4 5 PROMISE.resolve( new PROMISE((res, rej) => rej("ashuntefannao")) ).then(null, (reason) => console.log(`rejected__${reason}`)); PROMISE.resolve("ashun").then((result) => console.log(result));
REJECT 封装思想和resolve
静态方法相同
1 2 3 4 5 6 7 8 9 static reject(reason) { return new PROMISE((resolve, reject) => { if (reason instanceof PROMISE) { reason.then(resolve, reject); } else { reject(reason); } }); }
测试
1 2 3 4 5 6 PROMISE.reject("rejected").then(null, (err) => { console.log(err); }); PROMISE.reject(PROMISE.resolve("阿顺特烦恼")).then((val) => { console.log(val); });
ALL 原生Promise的静态方法all
接收一个PromiseArray,并按顺序对PromiseArray中的promise进行判断和处理
若存在一个promise没有确定状态,则all返回的Promise也为pending状态
若存在一个promise状态为rejected,则all返回的Promise也为rejected状态,并且后续的then能够接收到拒绝状态的promise传值。
若所有promise状态都为fulfilled,则返回一个有序的、元素为promise结果的数组
由于all是有序处理,所以我们需要通过遍历,按顺序处理业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static all(PROMISEarr) { let resolveArr = []; let test = true; return new PROMISE((resolve, reject) => { for (let promise of PROMISEarr) { let isPending = promise.status === PROMISE.PENDING; let isReject = promise.status === PROMISE.REJECTED; if (isPending) { test = false; break; } else if (isReject) { test = false; promise.then(null, (reason) => reject(reason)); break; } else { promise.then((res) => resolveArr.push(res)); } } test && resolve(resolveArr); }); }
下例可自行改变某个promise的状态,来检测不同结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let p1 = new PROMISE((res, rej) => { res("p1 stastus fulfilled"); }); let p2 = new PROMISE((res, rej) => { res("p2 stastus fulfilled"); }); let p3 = new PROMISE((res, rej) => { res("p3 stastus fulfilled"); }); let arr = [p1, p2, p3]; let all = PROMISE.all(arr).then( (result) => console.log(result), (reason) => console.log(reason) ); setTimeout(() => { console.log(all); }, 100);
RACE
race(PromiseArray)
赛跑,哪个Promise优先确认状态,就返回哪个Promise
一开始我们就已经实现了promise状态一经确定,就不可再改变
所以在实现的时候,我们只需循环调用每一个promise的then方法,哪个最先确认状态,就会优先执行then,我们可以通过then的两个回调函数,来改变当前默认返回的Promsie的状态。
1 2 3 4 5 6 7 static race(PROMISEarr) { return new PROMISE((resolve, reject) => { PROMISEarr.map((promise) => { promise.then(resolve, reject); }); }); }
下例可自行改变某个promise确认状态的延迟时间,来检测不同结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let p1 = new PROMISE((res, rej) => { setTimeout(() => { res("p1 stastus fulfilled"); }); }); let p2 = new PROMISE((res, rej) => { rej("p2 stastus rejected"); }); let p3 = new PROMISE((res, rej) => { setTimeout(() => { res("p3 stastus fulfilled"); }, 100); }); let arr = [p1, p2, p3]; let race = PROMISE.race(arr).then( (result) => console.log(`result__${result}`), (reason) => console.log(`reason__${reason}`) ); setTimeout(() => { console.log(race); }, 500);
allSettled
不在乎状态拒绝与否,所有的promise确认状态后,将会返回有序结果,且返回的promise状态为fulfilled。
若有一个promise没有确认状态,则allSettled默认返回的promise状态也为fulfilled,也就不会执行后续的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 static allSettled(PROMISEarr) { let results = []; let isPending = true; return new Promise((resolve, reject) => { for (let promise of PROMISEarr) { if (promise.status == PROMISE.PENDING) { isPending = true; break; } let status = promise.status; let value = promise.value; promise.then( (result) => { results.push({ status, value }); if (results.length == PROMISEarr.length) resolve(results); }, (reason) => { results.push({ status, reason: value }); if (results.length == PROMISEarr.length) resolve(results); } ); } }); }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let p1 = new PROMISE((res, rej) => { res("p1 stastus fulfilled"); }); let p2 = new PROMISE((res, rej) => { rej("p2 stastus rejected"); }); let p3 = new PROMISE((res, rej) => { res("p3 stastus fulfilled"); }); let arr = [p1, p2, p3]; let allSettled = PROMISE.allSettled(arr).then((result) => console.log(result) ); setTimeout(() => { console.log(allSettled); }, 500);
若有一个promise始终没有确认状态,则allSettled默认返回的promise状态也为pending。
1 2 3 4 5 …… let p2 = new PROMISE((res, rej) => { //rej("p2 stastus rejected"); }); ……