一,前言
上一篇,实现了 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 的处理;
评论