深入理解Promise(从功能角度解析promise源码实现)

一、什么是Promise?

Promise 是异步编程的一种解决方案。从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称回调地狱。

function ajaxA() {
    setTimeout(() => {
        let resultA = 10
        console.log("A请求返回结果===>", resultA)
    }, 1000)
}
function ajaxB() {
    setTimeout(() => {
        let resultB = 20
        console.log("B请求返回结果===>", resultB)
    }, 1000)
}
function ajaxC() {
    setTimeout(() => {
        let resultC = 30
        console.log("C请求返回结果===>", resultC)
    }, 1000)
}
function ajaxD() {
    setTimeout(() => {
        let resultD = 40
        console.log("D请求返回结果===>", resultD)
    }, 1000)
}

//回调嵌套(回调地狱)
setTimeout(() => {
    let resultA = 10
    console.log("A请求返回结果===>", resultA)
    setTimeout(() => {
        let resultB = 20
        console.log("B请求返回结果===>", resultB)
        setTimeout(() => {
            let resultC = 30
            console.log("C请求返回结果===>", resultC)
            setTimeout(() => {
                let resultD = 40
                console.log("D请求返回结果===>", resultD)
                console.log("回调嵌套最终返回结果======>", resultA + resultB + resultC + resultD)
            }, 1000)
        }, 1000)
    }, 1000)
}, 1000)

promise是用来解决两个问题的:

  1. 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  2. promise可以支持多个并发的请求,获取并发请求中的数据
  3. 这个promise可以解决异步的问题,本身不能说promise是异步的
function ajaxA() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let resultA = 10
            console.log("A_请求返回结果===>", resultA)
            resolve(resultA)
        }, 1000)
    })
}
function ajaxB() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let resultB = 20
            console.log("B_请求返回结果===>", resultB)
            resolve(resultB)
        }, 1000)
    })
}
function ajaxC() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let resultC = 30
            console.log("C_请求返回结果===>", resultC)
            resolve(resultC)
        }, 1000)
    })
}
function ajaxD() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let resultD = 40
            console.log("D_请求返回结果===>", resultD)
            resolve(resultD)
        }, 1000)
    })
}

let resultA = 0
let resultB = 0
let resultC = 0
let resultD = 0
ajaxA().then(resA => {
	resultA = resA
	return ajaxB()
}).then(resB => {
	resultB = resB
	return ajaxC()
}).then(resC => {
	resultC = resC
	return ajaxD()
}).then(resD => {
	resultD = resD
	console.log("使用promise最终返回结果======>", resultA + resultB + resultC + resultD)
})

二、什么是 Promise A+ 规范?

看到 A+ 肯定会想到是不是还有 A,事实上确实有。其实 Promise 有多种规范,除了前面的 Promise A、promise A+ 还有 Promise/B,Promise/D。目前我们使用的 Promise 是基于 Promise A+ 规范实现的,感兴趣的移步 promiseA+规范 了解一下,这里不赘述。

检验一份手写 Promise 靠不靠谱,通过 Promise A+ 规范自然是基本要求,这里我们可以借助 promises-aplus-tests 来检测我们的代码是否符合规范。

三、手写promise(实现原理)

基础用法

const promise = new Promise((resolve, reject) => {
   resolve('success')
   reject('err')
})
promise.then(res => {
  console.log('resolve', res)
}, err => {
  console.log('reject', err)
})

分析

  1. Promise 是一个类,在执行这个类的时候会传入一个执行器,这个执行器会立即执行
  2. Promise 会有三种状态 :Pending(等待)、Fulfilled(完成)、Rejected(失败)
  3. 状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改
  4. Promise 中使用 resolve 和 reject 两个函数来更改状态
  5. then 方法内部做的事情就是状态判断:状态成功就调用成功回调函数,状态失败则调用失败回调函数

1、简单实现

新建 MyPromise 类,传入执行器 executor

// myPromise.js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'

class MyPromise {
	status = PENDING; //status储存状态,初始值是 pending(等待)
	value = null; //value成功后保存成功的结果
	reason = null; //reason失败后保存失败的原因

	constructor(executor) {
		// executor是一个执行器,通过 new 命令创建对象实例时,自动调用该方法
		// 并传入resolve和reject方法
		executor(this.resolve, this.reject)
	}
	// resolve和reject为什么要用箭头函数?
	// 如果直接调用的话,普通函数this指向的是window或者undefined
	// 用箭头函数就可以让this指向当前实例对象
	// 更改成功后的状态
	resolve = (value) => {
		// 只有状态是等待,才执行状态修改
		if (this.status === PENDING) {
			// 状态修改为成功
			this.status = FULFILLED;
			// 保存成功之后的值
			this.value = value;
		}
	}
	// 更改失败后的状态
	reject = (reason) => {
		// 只有状态是等待,才执行状态修改
		if (this.status === PENDING) {
			// 状态成功为失败
			this.status = REJECTED;
			// 保存失败后的原因
			this.reason = reason;
		}
	}
	// then方法传递两个回调函数作为参数
	then(onFulfilled, onRejected) {
		// 判断状态
		if (this.status === FULFILLED) {
			// 调用成功回调,并且把值返回
			onFulfilled(this.value);
		} else if (this.status === REJECTED) {
			// 调用失败回调,并且把原因返回
			onRejected(this.reason);
		}
	}
}

// 使用 module.exports 对外暴露 MyPromise 类
module.exports = MyPromise;

测试

//test.js
// 引入我们的 MyPromise.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
	resolve('success')
	reject('err')
})
promise.then(value => {
	console.log('resolve', value)
}, reason => {
	console.log('reject', reason)
})
// 执行结果
// resolve success

2、异步任务问题

上面执行同步代码时没有问题,但是如果含有异步任务就会出现问题,例如:

//test.js
const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
		resolve('success')
	},1000)
})
myPromiseTest.then(value => {
	console.log('resolve', value)
}, reason => {
	console.log('reject', reason)
})

//此时不会有打印信息

原因分析:

由于 主线程代码立即执行,而 setTimeout 是异步代码,会隔一段时间才执行resolve方法从而去改变promise的状态,所以此时promise的状态为Pending(等待),但是then方法里面并没有对状态为Pending时的情况做判断。

代码改造:

1、MyPromise 类中新增两个变量分别用于存储成功和失败的回调

// 存储成功回调函数
onFulfilledCallback = null;
// 存储失败回调函数
onRejectedCallback = null;

2、then 方法中对 Pending 状态进行处理

then(onFulfilled, onRejected) {
	// 判断状态
	if (this.status === FULFILLED) {
		// 调用成功回调,并且把值返回
		onFulfilled(this.value);
	} else if (this.status === REJECTED) {
		// 调用失败回调,并且把原因返回
		onRejected(this.reason);
	} else if (this.status === PENDING) {
		// ==== 新增 ====
		// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
		// 等到执行成功失败函数的时候再传递
		this.onFulfilledCallback = onFulfilled;
		this.onRejectedCallback = onRejected;
	}
}

3、在resolve 与 reject 中调用回调函数

// 更改成功后的状态
resolve = (value) => {
	// 只有状态是等待,才执行状态修改
	if (this.status === PENDING) {
		// 状态修改为成功
		this.status = FULFILLED;
		// 保存成功之后的值
		this.value = value;
		// ==== 新增 ====
		// 判断成功回调是否存在,如果存在就调用
		this.onFulfilledCallback && this.onFulfilledCallback(value);
	}
}
// 更改失败后的状态
reject = (reason) => {
	// 只有状态是等待,才执行状态修改
	if (this.status === PENDING) {
		// 状态成功为失败
		this.status = REJECTED;
		// 保存失败后的原因
		this.reason = reason;
		// ==== 新增 ====
		// 判断失败回调是否存在,如果存在就调用
		this.onRejectedCallback && this.onRejectedCallback(reason)
	}
}

代码测试(处理异步任务)

const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
	setTimeout(() => {
		resolve('success')
	}, 2000);
})
myPromiseTest.then(value => {
	console.log('resolve', value)
}, reason => {
	console.log('reject', reason)
})

// 等待 2s 输出 resolve success

3、多个异步任务的处理

改造完以后发现可以处理异步任务了,但是由于then方法可以多次调用,按照上面的写法一旦遇到多个异步任务时就会出现问题,例如:

const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
    //resolve('同步success!')
	setTimeout(() => {
		resolve('异步success!')
	}, 2000);
})
myPromiseTest.then(value => {
  console.log('这是第一个then方法!')
  console.log('resolve', value)
})
myPromiseTest.then(value => {
  console.log('这是第二个then方法!')
  console.log('resolve', value)
})
myPromiseTest.then(value => {
  console.log('这是第三个then方法!')
  console.log('resolve', value)
})
// 执行结果
// 这是第三个then方法!
// resolve success!

原因分析:

由于只设置了一个变量用于保存回调函数,所以在多个then方法调用的时候,用于存放回调函数的变量在赋值的时候后面的会把前面的覆盖,导致最终resolve执行取到的回调函数则是最后一个then方法里面的内容。分析下来需要改进的地方就是分别保存每个异步任务的回调。

代码改造:

1、由于可能存在多个异步任务回调,用数组的形式保存

// 存储成功回调函数
// onFulfilledCallback = null;
onFulfilledCallbacks = [];
// 存储失败回调函数
// onRejectedCallback = null;
onRejectedCallbacks = [];

2、回调函数存入数组中

then(onFulfilled, onRejected) {
	// 判断状态
	if (this.status === FULFILLED) {
		// 调用成功回调,并且把值返回
		onFulfilled(this.value);
	} else if (this.status === REJECTED) {
		// 调用失败回调,并且把原因返回
		onRejected(this.reason);
	} else if (this.status === PENDING) {
		// 因为不知道后面状态的变化,这里先将成功回调和失败回调存储起来
		// 等待后续调用
		this.onFulfilledCallbacks.push(onFulfilled);
		this.onRejectedCallbacks.push(onRejected);
	}
}

3、循环调用成功和失败的回调

// 更改成功后的状态
resolve = (value) => {
	// 只有状态是等待,才执行状态修改
	if (this.status === PENDING) {
		// 状态修改为成功
		this.status = FULFILLED;
		// 保存成功之后的值
		this.value = value;
		// resolve里面将所有成功的回调拿出来执行
		while (this.onFulfilledCallbacks.length) {
			// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
			this.onFulfilledCallbacks.shift()(value)
		}
	}
}
// 更改失败后的状态
reject = (reason) => {
	// 只有状态是等待,才执行状态修改
	if (this.status === PENDING) {
		// 状态成功为失败
		this.status = REJECTED;
		// 保存失败后的原因
		this.reason = reason;
		// ==== 新增 ====
		// resolve里面将所有失败的回调拿出来执行
		while (this.onRejectedCallbacks.length) {
			this.onRejectedCallbacks.shift()(reason)
		}
	}
}

再次测试运行

const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
    //resolve('同步success!')
	setTimeout(() => {
		resolve('异步success!')
	}, 2000);
})
myPromiseTest.then(value => {
  console.log('这是第一个then方法!')
  console.log('resolve', value)
})
myPromiseTest.then(value => {
  console.log('这是第二个then方法!')
  console.log('resolve', value)
})
myPromiseTest.then(value => {
  console.log('这是第三个then方法!')
  console.log('resolve', value)
})
// 执行结果
// 这是第一个then方法!
// resolve success!
// 这是第二个then方法!
// resolve success!
// 这是第三个then方法!
// resolve success!

4、then方法链式调用

原生 promise 案例:

const promiseTest = new Promise((resolve, reject) => {
	resolve('success!')
})
promiseTest.then(res => {
	console.log("第一个then返回结果", res)
	return 'other success!'
}).then(res => {
	console.log("第二个then返回结果", res)
})
// 执行结果
// 第一个then返回结果 success!
// 第二个then返回结果 other success!

myPromise 测试:

const myPromiseTest = new MyPromise((resolve, reject) => {
	resolve('success!')
})
myPromiseTest.then(res => {
	console.log("第一个then返回结果",res)
	return 'other success!'
}).then(res=>{
	console.log("第二个then返回结果",res)
})
// 执行结果
// (报错信息) TypeError: Cannot read property 'then' of undefined

then 方法要实现链式调用,就需要方法里面返回一个 Promise 对象。then 方法里面 return 一个返回值作为下一个 then 方法的参数,如果是 return 一个 Promise 对象,那么就需要判断它的状态

代码改造:

class MyPromise {
	//......
	then(onFulfilled, onRejected) {
		// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
		const newPromise = new MyPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				const x = onFulfilled(this.value);
				resolve(x)
			} else if (this.status === REJECTED) {
				// ......
			} else {
				// ......
			}
		})
		return newPromise;
	}
}

修改后再次测试:

const myPromiseTest = new MyPromise((resolve, reject) => {
	resolve('success!')
})
myPromiseTest.then(res => {
	console.log("第一个then返回结果",res)
	return 'other success!'
}).then(res => {
	console.log("第二个then返回结果",res)
})
// 执行结果
// 第一个then返回结果 success!
// 第二个then返回结果 other success!

5、判断 then 方法返回的内容

判断当前then回调的的执行结果值是普通对象还是Promise对象

  • 如果是普通值,直接调用resolve
  • 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject

代码修改:

class MyPromise {
	......
	then(onFulfilled, onRejected) {
		const newPromise = new MyPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				const x = onFulfilled(this.value);
				// 将执行结果传入 resolvePromise 集中处理
				resolvePromise(x, resolve, reject);
			} else if (this.status === REJECTED) {
				// ......
			} else if (this.status === PENDING) {
				// ......
			}
		})
		return newPromise;
	}
}
function resolvePromise(x, resolve, reject) {
    // 判断当前then回调的执行结果值是普通对象还是Promise对象
    // 如果是普通值,直接调用resolve
    // 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
	if (x instanceof MyPromise) {
		// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
		// x.then(value => resolve(value), reason => reject(reason))
		// 简化之后
		x.then(resolve, reject)
	} else {
        // 普通值
		resolve(x)
	}
}

**注意点:then回调的执行结果返回的promise不可以是它本身,会发生循环调用 , 这个时候程序就会报错 **

原生 promise 案例:

const promiseTest = new Promise((resolve, reject) => {
	resolve(100)
})
const p1 = promiseTest.then(res => {
	console.log(res)
	return p1
})
// 执行结果
// 100
// (报错信息) Uncaught (in promise) TypeError: Chaining cycle detected for promise #

myPromise 实现

class MyPromise {
	......
	then(onFulfilled, onRejected) {
		const newPromise = new MyPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				const x = onFulfilled(this.value);
				// resolvePromise 集中处理,将 newPromise 传入
				resolvePromise(newPromise, x, resolve, reject);
			} else if (this.status === REJECTED) {
				// ......
			} else if (this.status === PENDING) {
				// ......
			}
		})
		return newPromise;
	}
}
function resolvePromise(newPromise, x, resolve, reject) {
    // 如果返回的 promise 对象是它本身,抛出类型错误
	if (newPromise === x) {
		reject(new TypeError("Chaining cycle detected for promise #"));
	}
	if (x instanceof MyPromise) {
		x.then(resolve, reject)
	} else {
		resolve(x)
	}
}

运行一下:

const myPromiseTest = new MyPromise((resolve, reject) => {
	resolve(100)
})
const p1 = myPromiseTest.then(res => {
	console.log(res)
	return p1
})
// 执行结果
// 100
// (报错信息) Uncaught ReferenceError: Cannot access 'p1' before initialization

报错信息提示 p1 未初始化,原因在于 resolvePromise(newPromise, x, resolve, reject) 传递的参数 newPromise 此时还未初始化完成,所以这里用一个 宏任务或者微任务 来创建一个异步函数去等待 newPromise 完成初始化

代码修改:

then(onFulfilled, onRejected) {
    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise = new MyPromise((resolve, reject) => {
        // 这里的内容在执行器中,会立即执行
        if (this.status === FULFILLED) {
            // 创建一个宏任务(setTimeout)等待 promise 完成初始化
            setTimeout(()=>{
                // 获取成功回调函数的执行结果
                const x = onFulfilled(this.value);
                // 传入 resolvePromise 集中处理
                resolvePromise(promise, x, resolve, reject);
            },0)
        } else if (this.status === REJECTED) {
            // ......
        } else if (this.status === PENDING) {
            // ......
        }
    })
    return promise;
}

修改后重新运行

const myPromiseTest = new MyPromise((resolve, reject) => {
	resolve(100)
})
const p1 = myPromiseTest.then(res => {
	console.log(res)
	return p1
})
// 执行结果和原生promise一致
// 100
// (报错信息) Uncaught (in promise) TypeError: Chaining cycle detected for promise #

6、错误捕捉

  • 捕获执行器错误

捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败

constructor(executor) {
	try {
         // executor是一个执行器,进入会立即执行
		// 并传入resolve和reject方法
		executor(this.resolve, this.reject)
	} catch (error) {
		// 如果有错误,就直接执行 reject
		this.reject(error)
	}
}

测试:

const myPromiseTest = new MyPromise((resolve, reject) => {
	throw new Error('执行器错误')
})
myPromiseTest.then(res => {
	console.log(res)
},err => {
	console.log(err)
})
// 执行结果
// Error: 执行器错误
  • then 执行的时错误捕获
then(onFulfilled, onRejected) {
    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise = new MyPromise((resolve, reject) => {
        // 这里的内容在执行器中,会立即执行
        if (this.status === FULFILLED) {
            // 创建一个宏任务(setTimeout)等待 promise 完成初始化
            setTimeout(() => {
                try {
                    // 获取成功回调函数的执行结果
                    const x = onFulfilled(this.value);
                    // 传入 resolvePromise 集中处理
                    resolvePromise(newPromise, x, resolve, reject);
                } catch (error) {
                    reject(error)
                }
            }, 0)
        } else if (this.status === REJECTED) {
            // ......
        } else if (this.status === PENDING) {
            // ......
        }
    })
    return promise;
}

测试:( 第一个then方法中的错误要在第二个then方法中捕获到 )

const myPromiseTest = new MyPromise((resolve, reject) => {
	resolve("success")
})
myPromiseTest.then(res => {
	console.log("第1个then方法返回结果===>", res)
	throw new Error('error!!!')
}, err => {
	console.log("第1个then方法返回错误信息===>", err)
}).then(res => {
	console.log("第2个then方法返回结果===>", res)
}, err => {
	console.log("第2个then方法返回错误信息===>", err)
})
// 执行结果
// 第1个then方法返回结果===> success
// 第2个then方法返回错误信息===> Error: error!!!

7、其他两个状态进行改造

根据 fulfilled 状态下的处理,对 rejected 和 pending 状态进行改造

then(onFulfilled, onRejected) {
    const newPromise = new MyPromise((resolve, reject) => {
        if (this.status === FULFILLED) {
            setTimeout(() => {
                try {
                    const x = onFulfilled(this.value);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch (error) {
                    reject(error)
                }
            }, 0)
        } else if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    const x = onRejected(this.reason);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch (error) {
                    reject(error)
                }
            }, 0)
        } else if (this.status === PENDING) {
            this.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(newPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                },0)
            });
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(newPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                },0)
            });
        }
    })
    return newPromise;
}

8、then 方法参数变为可选

以上写的都是默认传入 onFulfilled、onRejected 两个回调函数,但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。

例如:

new Promise((resolve, reject) => {
  resolve("success")
}).then().then().then().then(res => console.log(res))

// 执行结果  success

then 方法改造

then(onFulfilled, onRejected) {
	// 成功函数处理 忽略函数之外的其他值
	onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
	// 失败函数处理 忽略函数之外的其他值 抛出异常  实现catch冒泡的关键
	onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
	
	// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
	const newPromise = new MyPromise((resolve, reject) => {
		// ......
	})
}

测试:

// 验证resolve
new MyPromise((resolve, reject) => {
	resolve("resolve success")
}).then().then().then().then(res => console.log("结果===>", res))
// 执行结果 
// 结果===> resolve success

// 验证reject
new MyPromise((resolve, reject) => {
	reject("reject error")
}).then().then().then().then(res => console.log("结果===>", res), err => console.log("错误===>", err))
// 执行结果 
// 错误===> reject error

9、实现 resolve 和 reject 的静态调用

原生 promise 案例:

Promise.resolve().then(res => {
	console.log("two")
})
console.log("one")
// 执行结果
// one
// two

Promise.resolve()用于将现有对象转换为Promise对象,从而控制异步流程。

代码实现

// resolve 静态方法
static resolve(value) {
    // 如果是MyPromise 实例 则直接返回
    if (value instanceof MyPromise) return value;
    // 如果是MyPromise 实例 否则返回一个 MyPromise实例
    return new MyPromise((resolve) => resolve(value));
}
// reject 静态方法
static reject(reason) {
    // 如果是MyPromise 实例 则直接返回
    if (reason instanceof MyPromise) return reason;
    // 如果是MyPromise 实例 否则返回一个 MyPromise实例
    return new MyPromise((resolve, reject) => reject(reason));
}

10、其他静态方法

Promise.all:

Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。Promise.all 获得的成功结果的数组里面的数据顺序和 Promise.all 接收到的数组顺序是一致的,即 p1 的结果在前,即便 p1 的结果获取的比 p2 要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all 毫无疑问可以解决这个问题。

Promise.race

顾名思义,Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3]) 里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。 该方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

总结:手写后的学习理解

let p1 = Promise.resolve(42)
p1.then((value) => {
	//第一种情况,返回一个Promise
	return new Promise(function(resolve, rejected) {
		resolve(value + 1)
	})

	//第二种情况,返回一个值
	return value + 2;

	//第三种情况,新建一个promise,使用reslove返回值
	const p2 = new Promise(function(resolve, rejected) {
		resolve(value + 3)
	})

	//第四种情况,新建一个promise,使用return返回值
	const p2 = new Promise(function(resolve, rejected) {
		return (value + 4)
	})

	//第五种情况,没有返回值
	return undefined
}).then((value) => {
	console.log(value)
})

按照学习理解:

第一种情况,新建promise的resolve传出的值将作为then方法返回的promise的resolve的值传递出,console将打印出43

第二种情况,return的值将作为then方法返回的promise的resolve的值传递出,console将打印出44

第三种情况,虽然新建了promise,但对于then方法来说,没有向它返回的promise传递返回值,console将打印出undifined

第四种情况,同第三种情况,

第五种情况,then方法没有返回值,then方法的promise的resolve的值将传递出undifined。

以上三、四、五种情况,其实都是一样的问题,构造then方法的函数没有向then方法返回的promise对象的resolve方法传递值。因此resolve返回的都是undfined

代码:手写 Promise

// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'

class MyPromise {
	status = PENDING; //status储存状态,初始值是 pending(等待)
	value = null; //value成功后保存成功的结果
	reason = null; //reason失败后保存失败的原因
	onFulfilledCallbacks = [];// 存储成功回调函数
	onRejectedCallbacks = [];// 存储失败回调函数

	constructor(executor) {
		try {
			// executor是一个执行器,进入会立即执行
			// 并传入resolve和reject方法
			executor(this.resolve, this.reject)
		} catch (error) {
			// 如果有错误,就直接执行 reject
			this.reject(error)
		}
	}
	
	// resolve和reject为什么要用箭头函数?
	// 如果直接调用的话,普通函数this指向的是window或者undefined
	// 用箭头函数就可以让this指向当前实例对象
	
	// 更改成功后的状态
	resolve = (value) => {
		// 只有状态是等待,才执行状态修改
		if (this.status === PENDING) {
			// 状态修改为成功
			this.status = FULFILLED;
			// 保存成功之后的值
			this.value = value;
			// ==== 新增 ====
			// resolve里面将所有成功的回调拿出来执行
			while (this.onFulfilledCallbacks.length) {
				// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
				this.onFulfilledCallbacks.shift()(value)
			}
		}
	}
	// 更改失败后的状态
	reject = (reason) => {
		// 只有状态是等待,才执行状态修改
		if (this.status === PENDING) {
			// 状态成功为失败
			this.status = REJECTED;
			// 保存失败后的原因
			this.reason = reason;
			// ==== 新增 ====
			// resolve里面将所有失败的回调拿出来执行
			while (this.onRejectedCallbacks.length) {
				this.onRejectedCallbacks.shift()(reason)
			}
		}
	}
	// then方法传递两个回调函数作为参数
	then(onFulfilled, onRejected) {
		// 成功函数处理 忽略函数之外的其他值
		onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
		// 失败函数处理 忽略函数之外的其他值 抛出异常  实现catch冒泡的关键
		onRejected = typeof onRejected === 'function' ? onRejected : reason => {
			throw reason
		}

		// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
		const newPromise = new MyPromise((resolve, reject) => {
			// 这里的内容在执行器中,会立即执行
			if (this.status === FULFILLED) {
				// 创建一个宏任务(setTimeout)等待promise完成初始化
				setTimeout(() => {
					try {
						// 获取成功回调函数的执行结果
						const x = onFulfilled(this.value);
						// 传入 resolvePromise 集中处理
						resolvePromise(newPromise, x, resolve, reject);
					} catch (error) {
						reject(error)
					}
				}, 0)
			} else if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						// 获取失败回调函数的执行结果
						const x = onRejected(this.reason);
						// 传入 resolvePromise 集中处理
						resolvePromise(newPromise, x, resolve, reject);
					} catch (error) {
						reject(error)
					}
				}, 0)
			} else if (this.status === PENDING) {
				// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
				// 等到执行成功失败函数的时候再传递
				this.onFulfilledCallbacks.push(() => {
					setTimeout(() => {
						try {
							// 获取成功回调函数的执行结果
							const x = onFulfilled(this.value);
							// 传入 resolvePromise 集中处理
							resolvePromise(newPromise, x, resolve, reject);
						} catch (error) {
							reject(error)
						}
					}, 0)
				});
				this.onRejectedCallbacks.push(() => {
					setTimeout(() => {
						try {
							// 调用失败回调,并且把原因返回
							const x = onRejected(this.reason);
							// 传入 resolvePromise 集中处理
							resolvePromise(newPromise, x, resolve, reject);
						} catch (error) {
							reject(error)
						}
					}, 0)
				});
			}
		})
		return newPromise;
	}

	// resolve 静态方法
	static resolve(value) {
		// 如果是MyPromise 实例 则直接返回
		if (value instanceof MyPromise) return value;
		// 如果是MyPromise 实例 否则返回一个 MyPromise实例
		return new MyPromise((resolve) => resolve(value));
	}

	// reject 静态方法
	static reject(reason) {
		// 如果是MyPromise 实例 则直接返回
		if (reason instanceof MyPromise) return reason;
		// 如果是MyPromise 实例 否则返回一个 MyPromise实例
		return new MyPromise((resolve, reject) => reject(reason));
	}
}

function resolvePromise(newPromise, x, resolve, reject) {
    // 判断当前then回调的执行结果值是普通对象还是Promise对象
    // 如果是普通值,直接调用resolve
    // 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
	// 如果return的Promise是它自己,抛出类型错误并返回
	if (newPromise === x) {
        reject(new TypeError("Chaining cycle detected for promise #"));
	}
	if (x instanceof MyPromise) {
		x.then(resolve, reject)
	} else {
		resolve(x)
	}
}

module.exports = MyPromise;

代码:去除冗余

/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 初始状态
const PENDING = "pending";
// 完成状态
const FULFILLED = "fulfilled";
// 失败状态
const REJECTED = "rejected";

// 异步执行方法封装
function asyncExecFun(fn) {
	setTimeout(() => fn(), 0);
}

// 执行promise resolve功能
function resolvePromise(promise, res, resolve, reject) {
	// 返回同一个promise
	if (promise === res) {
		reject(new TypeError("Chaining cycle detected for promise #"));
		return;
	}
	// promise结果
	if (res instanceof MyPromise) {
		res.then(resolve, reject);
	} else {
		// 非promise结果
		resolve(res);
	}
}

/**
 * 1. 是个构造函数
 * 2. 传入一个可执行函数 函数的入参第一个为 fullFill函数 第二个为 reject函数;  函数立即执行,  参数函数异步执行
 * 3. 状态一旦更改就不可以变更  只能 pending => fulfilled 或者  pending => rejected
 * 4. then 的时候要处理入参的情况 successCallback 和failCallback 均可能为非函数
 *      默认的 failCallback 一定要将异常抛出, 这样下一个promise便可将其捕获 异常冒泡的目的
 * 5. then 中执行回调的时候要捕获异常 将其传给下一个promise
 *    如果promise状态未变更 则将回调方法添加到对应队列中
 *    如果promise状态已经变更 需要异步处理成功或者失败回调
 *    因为可能出现 回调结果和当前then返回的Promise一致 从而导致死循环问题
 * 6. catch只是then的一种特殊的写法 方便理解和使用
 * 7. finally 特点 1. 不过resolve或者reject都会执行
 *                2. 回调没有参数
 *                3. 返回一个Promise 且值可以穿透到下一个then或者catch
 * 8. Promise.resolve, Promise.reject 根据其参数返回对应的值 或者状态的Promise即可
 * 9. Proise.all 特点  1. 返回一个Promise
 *                    2. 入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致
 *                    3. 所有的值或者promise都完成才能resolve 所有要计数
 *                    4. 只要有一个为reject 返回的Promise便reject
 * 10. Proise.race 特点 1. 返回一个Promise
 *                    2. 入参是数组 那么出参根据第一个成功或者失败的参数来确定
 *                    3. 只要有一个resolve 或者reject 便更改返回Promise的状态
 *
 *
 */

class MyPromise {
	status = PENDING;
	value = undefined;
	reason = undefined;
	successCallbacks = [];
	failCallbacks = [];
	constructor(exector) {
		// 立即执行传入参数
		// 参数直接写为 this.resolve  会导致函数内 this指向会发生改变
		// 异步执行状态变更
		// 捕获执行器的异常
		try {
			exector(
				(value) => asyncExecFun(() => this.resolve(value)),
				(reason) => asyncExecFun(() => this.reject(reason))
			);
		} catch (e) {
			this.reject(e)
		}
	}

	resolve(value) {
		// 如果状态已经变更则直接返回
		if (this.status !== PENDING) return;
		this.value = value;
		this.status = FULFILLED;
		// 执行所有成功回调
		while (this.successCallbacks.length) this.successCallbacks.shift()();
	}

	reject(reason) {
		// 如果状态已经变更则直接返回
		if (this.status !== PENDING) return;
		this.reason = reason;
		this.status = REJECTED;
		if (!this.failCallbacks.length) {
			throw '(in MyPromise)'
		}
		// 执行所有失败回调
		while (this.failCallbacks.length) this.failCallbacks.shift()();
	}
	then(successCallback, failCallback) {
		// 成功函数处理 忽略函数之外的其他值
		successCallback = typeof successCallback == "function" ? successCallback : (v) => v;
		// 失败函数处理 忽略函数之外的其他值 抛出异常  实现catch冒泡的关键
		failCallback = typeof failCallback == "function" ? failCallback : (reason) => {
			throw reason
		};

		let promise = new MyPromise((resolve, reject) => {
			// 统一异常处理逻辑
			const execFun = (fn, val) => {
				try {
					let res = fn(val);
					resolvePromise(promise, res, resolve, reject);
				} catch (e) {
					reject(e);
				}
			};
			// 执行成功回调
			const execSuccessCallback = () => execFun(successCallback, this.value);
			// 执行失败回调
			const execFailCallback = () => execFun(failCallback, this.reason);
			// 同步将对应成功或者失败回调事件加入对应回调队列
			if (this.status === PENDING) {
				// 将成功回调加入队列
				this.successCallbacks.push(execSuccessCallback);
				// 讲失败回调加入队列
				this.failCallbacks.push(execFailCallback);
				return;
			}
			// 延迟执行 可以将函数执行结果和当前then 返回的promise 进行比较
			asyncExecFun(() => {
				// 如果已经 fulfilled 可直接调用成功回调方法
				if (this.status === FULFILLED) {
					execSuccessCallback();
					// 如果已经 rejected 可直接调用失败回调方法
				} else if (this.status === REJECTED) {
					execFailCallback();
				}
			});
		});
		return promise;
	}

	catch (failCallback) {
		return this.then(undefined, failCallback)
	} 

	finally(callback) {
		return this.then(
			// 穿透正常值
			(value) => MyPromise.resolve(callback()).then(() => value),
			(reason) =>
			MyPromise.resolve(callback()).then(() => {
				// 穿透异常信息
				throw reason;
			})
		)
	}

	static resolve(value) {
		// 如果是MyPromise 实例 则直接返回
		if (value instanceof MyPromise) return value;
		// 如果是MyPromise 实例 否则返回一个 MyPromise实例
		return new MyPromise((resolve) => resolve(value));
	}
	static reject(reason) {
		// 如果是MyPromise 实例 则直接返回
		if (reason instanceof MyPromise) return reason;
		// 如果是MyPromise 实例 否则返回一个 MyPromise实例
		return new MyPromise((resolve, reject) => reject(reason));
	}

	// all方法
	static all(array) {
		// 存储结果
		let result = [];
		// 存储数组长度
		let len = array.length;
		// 创建返回MyPromise
		let promise = new MyPromise((resolve, reject) => {
			// 定义当前MyPromise的索引
			let index = 0;
			// 添加数据的公用方法
			function addData(key, data) {
				// 赋值
				result[key] = data;
				// 索引递增
				index++;
				// 全部执行完则resolve
				if (index == len) {
					resolve(result);
				}
			}
			// 按顺序变量数组
			for (let i = 0; i < len; i++) {
				let curr = array[i];
				// 如果是MyPromise则 按其规则处理
				if (curr instanceof MyPromise) {
					curr.then((value) => addData(i, value), reject);
				} else {
					// 非MyPromise直接赋值
					addData(i, curr);
				}
			}
		});
		// 返回新的MyPromise实例
		return promise;
	}
	// 只要有一个成功或者失败就返回
	static race(array) {
		let promise = new MyPromise((resolve, reject) => {
			for (let i = 0; i < array.length; i++) {
				let curr = array[i];
				// MyPromise实例 结果处理
				if (curr instanceof MyPromise) {
					curr.then(resolve, reject);
				} else {
					// 非MyPromise实例处理
					resolve(curr);
				}
			}
		});
		return promise;
	}
}

module.exports = MyPromise;

你可能感兴趣的:(JavaScript,javascript,前端,vue.js)