一,前言
上一篇,实现了 Promise 对异步操作的支持,主要涉及以下几个点:
测试 Promise 对异步操作的支持;
分析当前 Promise 代码问题及解决方案;
使用发布订阅思想实现对异步操作的支持;
Promise 异步操作的测试;
本篇,继续将实现 Promise 的链式调用;
二,前文回顾
在前几篇中,简单介绍了 Promise 的使用;翻译 Promise A+ 规范;实现简版 Promise;
上一篇,利用发布订阅模式,实现了 Promise 对异步操作的支持,主要就是以下两步:
三,提出问题
omise 的一个核心特性,就是支持链式调用;
也因此,使用 Promise 能够很好的解决异步逻辑的“回调地狱”问题;
备注:
1,测试原生 Promise
情况 1,成功回调中,返回普通值
let promise = new Promise((resolve, reject) => { setTimeout(() => { // 异步操作 resolve('执行成功'); // 进入成功回调处理 }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value // return new Error() // 返回普通值 // return undefined // 返回普通值}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onFulfilled] then1-onFulfilled:执行成功
复制代码
现象:
1 秒后,执行了 then1 中的 onFulfilled 回调,继续 return value;
然后,进入 then2 中的 onFulfilled 回调,可以获取到 then1 传递的 value;
结论:
成功回调中返回普通值,会进入下一个 then 的成功回调;
情况 2,失败回调中,返回普通值
let promise = new Promise((resolve, reject) => { setTimeout(() => { reject("执行失败") // 进入失败回调处理 }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson) return "then1-onRejected:" + reson // 返回普通值 // return new Error() // 返回普通值 // return undefined // 返回普通值}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onRejected] 执行失败// [then2-onFulfilled] then1-onRejected:执行失败
复制代码
现象:
1 秒后,执行了 then1 中的 onRejected 回调,继续 return reson;
然后,进入 then2 中的 onFulfilled 回调,可以获取到 then1 传递的 value;(这个 value 就是 then1 中 return 的普通值)
结论:
失败回调中返回普通值,会进入下一个 then 的成功回调;
备注:
由于每个 promise 实例的状态只能被修改一次,而示例中失败态的 promise 在第二次 then 时进入了成功的回调,说明 then1 处理完成后,返回了一个全新的 promise 实例;
情况 3,成功回调中,抛出异常
let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("执行成功") }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) throw new Error("抛出异常")}, (reson) => { console.log('[then1-onRejected]', reson) return "then1-onRejected:" + reson}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onRejected] Error: 抛出异常
复制代码
现象:
1 秒后,执行了 then1 中的 onFulfilled 回调,抛出异常;
然后,进入 then2 中的 onRejected 回调,并捕获到 then1 抛出的异常;
结论:
成功回调中抛出异常,会进入下一个 then 的失败回调;
情况 4,失败回调中,抛出异常
let promise = new Promise((resolve, reject) => { setTimeout(() => { reject("执行失败") }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value)}, (reson) => { console.log('[then1-onRejected]', reson) throw new Error("抛出异常")}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onRejected] 执行失败// [then2-onRejected] Error: 抛出异常
复制代码
现象:
1 秒后,执行了 then1 中的 onRejected 回调,抛出异常;
然后,进入 then2 中的 onRejected 回调,并捕获到 then1 抛出的异常;
结论:
失败回调中抛出异常,会进入下一个 then 的失败回调;
情况 5,executor 执行器中报错
如果在 executor 中就抛出了异常,还没有执行 resolve 和 reject:
备注:同步逻辑报错,非异步操作;
let promise = new Promise((resolve, reject) => { throw new Error("executor 同步报错") // 抛出异常 resolve("处理成功") // 不会被执行})promise.then((value) => { console.log('[then1-onFulfilled]', value)}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})// [then1-onRejected] Error: executor 同步报错// [then2-onFulfilled] undefined
复制代码
现象:
executor 执行器函数中的同步操作抛出异常,进入 then1 的失败回调;
由于 then1 中没有 return,相当于返回了 undefined 普通值,进入 then2 的成功处理;
结论:
executor 同步逻辑抛出异常,进入 then 失败处理;
总结
根据以上 5 种情况进行总结:
Promise 的链式调用,当调用 then 方法后,将会返回一个全新的 promise 实例;
then 中方法(无论成功还是失败)返回普通值,都会做为下一次 then 的成功结果;
执行器函数 和 then 中方法(无论成功还是失败)抛出异常,都会进入下一次 then 的失败结果;
备注:
若 then 中方法返回一个 promise 实例,那么,将根据这个 promise 的状态决定执行成功还是失败的回调:
成功的 promise:将返回值传递给下一个 then 的成功回调;
失败的 promise:将失败原因传递给下一个 then 的失败回调;
2,测试手写 Promise
链式调用示例(与测试原生 Promise 代码相同):
let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('执行成功'); }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// TypeError: Cannot read property 'then' of undefined
复制代码
现象
3,问题分析
class Promise{ constructor(executor){ this.state = PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const reslove = (value) =>{...} const reject = (reason) =>{...} executor(reslove, reject); } then(onFulfilled, onRejected){ if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ onFulfilled(this.value) // 被调用后,没有返回新的 promise }) this.onRejectedCallbacks.push(()=>{ onRejected(this.value) // 被调用后,没有返回新的 promise }) } if(this.state === DULFILLED){ onFulfilled(this.value) // 被调用后,没有返回新的 promise } if(this.state === REJECTED){ onRejected(this.reason) // 被调用后,没有返回新的 promise } // then 处理完成后,并没有继续返回 promise 对象,所以不能继续 .then }}
module.exports = Promise;
复制代码
4,解决方案
在 then 方法的回调函数执行完成后,需要获取到函数的返回值,并包装成为全新的 promise 实例;
四,Promise 链式调用的实现
获取 then 回调处理返回值
class Promise{ constructor(executor){...} then(onFulfilled, onRejected){ if(this.state === DULFILLED){ // 拿到回调处理的返回值 let x = onFulfilled(this.value) } if(this.state === REJECTED){ let x = onRejected(this.reason) resolve(x) } if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ let x = onFulfilled(this.value) resolve(x) }) this.onRejectedCallbacks.push(()=>{ let x = onRejected(this.reason) resolve(x) }) } // 包装回调处理的返回值 x ,成为一个全新的 Promise 实例 let promise2 = new Promise((resolve, reject)=>{ // 问题:拿不到 x,不在一个作用域中 });
// 返回新的 promise 实例,这样外部可以继续调用 .then return promise2; }}
复制代码
问题
2,then 返回全新 promise
要解决上边的问题,就需要在 promise2 的 executor 执行器中拿到返回值 x;
class Promise{ constructor(executor){...} then(onFulfilled, onRejected){ // 获取成功/失败回调的返回值,并包装成为新的 Promise 实例 let promise2 = new Promise((resolve, reject)=>{ // 同步 if(this.state === DULFILLED){ // 拿到回调处理的返回值 let x = onFulfilled(this.value) // 更改 promise2 的状态,并返回这个新的 promise 实例(即 primise2) resolve(x) } // 同步 if(this.state === REJECTED){ let x = onRejected(this.reason) resolve(x) } // 异步 if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ let x = onFulfilled(this.value) resolve(x) }) this.onRejectedCallbacks.push(()=>{ let x = onRejected(this.reason) resolve(x) }) } });
// 返回新的 promise 实例 return promise2; }}
复制代码
3,对异常的处理
executor 执行器函数和 then 中方法(无论成功还是失败)抛出异常,都会进入下一次 then 的失败结果;
只需在以下几个位置捕获异常,并执行 reject 即可:
executor 执行器函数(在简版 Promsie 中已实现)
then 方法中的同步操作(DULFILLED 和 REJECTED),回调函数执行时;
then 方法中的异步操作(PENDING),回调函数执行时;
class Promise{ constructor(executor){ const reslove = (value) =>{...} const reject = (reason) =>{...} try{ executor(reslove, reject); }catch(e){ reject(e) // 捕获异常 } }
then(onFulfilled, onRejected){ let promise2 = new Promise((resolve, reject)=>{ if(this.state === DULFILLED){ try{ let x = onFulfilled(this.value) resolve(x) }catch(e){ reject(e) // 捕获异常 } } if(this.state === REJECTED){ try{ let x = onRejected(this.reason) resolve(x) }catch(e){ reject(e) // 捕获异常 } } if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ try{ let x = onFulfilled(this.value) resolve(x) }catch(e){ reject(e) // 捕获异常 } }) this.onRejectedCallbacks.push(()=>{ try{ let x = onRejected(this.reason) resolve(x) }catch(e){ reject(e) // 捕获异常 } }) } });
return promise2; }}
复制代码
4,测试手写 Promsie 的链式调用
let promise = new Promise((resolve, reject) => { setTimeout(() => { // 异步操作 resolve('执行成功'); // 成功态,传参 }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onFulfilled] then1-onFulfilled:执行成功
复制代码
略:5 种情况与原生 Promise 表现一致;
五,执行过程分析
分析当前 Promise 的执行过程:
首先,应用层 new Promise 创建 promise 实例时,传入的 executor 执行器会在 constructor 中被立即执行;
应用层调用 promise.then 时,传入成功/失败状态的处理逻辑;
进入 then 处理,在源码中,会先创建一个 promise2 实例;那么,promise2 的 executor 执行器也会被立即执行;
此时,原本 promise 对三态的处理,就被包裹在了 promise2 的 executor 中;因此,从执行上不会受到影响,同样会被立即执行;
这样,在包装 then 返回值 x 的 promise2 中,就可以拿到这个返回值 x 了!
当 promise2 调用了 resolve(x),就会进入外部 promise.then 的成功回调处理,并返回一个普通值;
源码中的 promise2 中就拿到了 promise.then 的返回值 x,并通过 reslove(x) 将 promise2 的状态更新为成功态;
然后,就进入了应用层 Promise 链式调用第二个 then 的成功回调处理。。。。
备注:
六,结尾
本篇,主要实现了 Promise 的链式调用功能,主要涉及以下几个点:
介绍了 Promise 链式调用,返回普通值和抛出异常的 5 种情况;
分析了当前 Promise 源码的问题及解决方案;
Promise 链式调用的实现、功能测试、执行过程分析;
备注:
下一篇,实现 Promise 返回值 x 的处理;
评论