promise对于前端来说用的最多的场景应该就是异步请求了
我参考大佬的文章自己写了一遍,每一步后面加入自己理解的小结
梳理完一遍后还是有很多收获的!😊
开始实现
一、核心逻辑实现
1.新建MyPromise类
js
// 新建MyPromise类
class MyPromise {
constructor(executor){
// executor是一个执行器,进入会立即执行
executor()
}
}
2.executor 传入 resolve 和 reject 方法
js
class MyPromise {
// 传入一个函数里面含有两个参数resolve和reject
constructor(executor){
executor(this.resolve,this.reject)
}
// resolve和reject在调用的时候执行
resolve = ()=>{}
reject = ()=>{}
}
3.状态与结果的管理
js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 传入一个函数里面含有两个参数resolve和reject
constructor(executor){
executor(this.resolve,this.reject)
}
// 储存状态的变量,初始值是 pending
status = PENDING;
// 成功之后的值
value = null;
// 失败之后的原因
reason = null;
// resolve和reject在调用的时候执行
// 更改成功后的状态
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 的简单实现
js
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value); }
else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
} }
5.使用 module.exports 对外暴露 MyPromise 类
js
// MyPromise.js
module.exports = MyPromise;
小结:
这里写了一个声明了几个变量
一个是promise类的构造函数需要一个参数,类型是函数,这个函数传参是resolve和reject
还声明了当前异步状态,成功和失败的值
后面实现了resolve和reject主要功能就是改变当前状态,然后存储数据
最后声明了一个then函数,传参是两个函数,第一个是成功的,第二个是失败,根据当前status状态,判断执行哪个函数
二、异步逻辑
上面实现了同步的成功函数、失败函数,和 then函数,但是如果有异步进程,那么promise函数还没有执行结果,then函数就会执行,这是有问题的,所以实现一下then的等待过程
js
class MyPromise {
// 传入一个函数里面含有两个参数resolve和reject
constructor(executor){
executor(this.resolve,this.reject)
}
// ====== 新增 ======
// 存储成功回调函数
onFulfilledCallback = null;
// 存储失败回调函数
onRejectedCallback = null;
// 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)
}
}
}
then中的函数修改
js
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;
} }
小结:
这里是怎么保证then在promise函数之后执行的呢?
我理解的是一上来先执行的就是then函数,因为一上来就是pending状态,所以存储then回调函数中传入的两个函数参数,然后在promise实例中的resolve和reject执行,实现then中的回调函数在promise之后执行
三、多个异步逻辑
上面实现了单个异步进程,如果存在多个then函数,该怎么处理异步呢?
js
// 存储成功回调函数
// onFulfilledCallback = null;
onFulfilledCallbacks = [];
// 存储失败回调函数
// onRejectedCallback = null;
onRejectedCallbacks = [];
在then函数的相应位置修改
js
// ==== 新增 ====
// 因为不知道后面状态的变化,这里先将成功回调和失败回调存储起来
// 等待后续调用
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
在promise成功和失败的函数中,分别执行下面程序
js
// resolve里面将所有成功的回调拿出来执行
// 成功
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(value)
}
// 失败
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason)
}
小结:
这里就是用数组存储要执行的函数,然后利用数组方法shift,依次执行。
当然,这里默认的都是同步函数,后面增加对异步函数的处理
★☆★☆★☆★☆★☆★☆在这里可以先执行下已完成的内容试试★☆★☆★☆★☆★☆★☆
四、实现链式调用
js
class MyPromise{
......
then(onFulfilled,onRejected){
// ===新增===
// 为了链式调用这里直接创建一个 MyPromise,并在后面return出去
const promise2 = new MyPromise((resolve,reject)=>{
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(x, resolve, reject);
}
else if (this.status === REJECTED) {
onRejected(this.reason);
}
else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
})
return promise2;
}
}
function resolvePromise(x, resolve, reject) {
// 判断x是不是 MyPromise 实例对象
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,并且在then里面返回这个promise,就可以实现链式调用
在这里声明了一个新的函数resolvePromise,传入的x也就是当前的then中的执行结果,成功的回调函数,失败的回调函数,如果是promise实例,就接着执行then函数,否则就存储当前值
五、then方法链式调用识别Promise是否返回自己
js
// MyPromise.js
class MyPromise {
......
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// ==== 新增 ====
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
})
} else if (this.status === REJECTED) {
......
})
return promise2;
}
}
js
function resolvePromise(promise2, x, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(x instanceof MyPromise) {
x.then(resolve, reject)
}
else{
resolve(x)
}
}
学习一个新的api
小结:
这一部分主要是传入当前promise及返回的promise,判断是否相等,
如果相等的话,就抛出异常,避免循环调用。 这里用了queueMicrotask这个api,创建一个微任务,等待当前任务队列的执行结束后,立马执行微任务队列的内容,
具体到这里也就是,等待promise2这个初始化结束后,再传入到resolvePromise里面
六、捕获错误及 then链式调用其他状态代码补充
- 捕获执行器错误
js
// MyPromise.js
constructor(executor){
// ==== 新增 ====
// executor 是一个执行器,进入会立即执行
// 并传入resolve和reject方法
try {
executor(this.resolve, this.reject)
} catch (error) {
// 如果有错误,就直接执行 reject
this.reject(error)
}
}
2.then 执行的时错误捕获
js
// MyPromise.js
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
// ==== 新增 ====
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
} else if (this.status === PENDING) {
// 等待
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
})
return promise2;
}
小结:
这里主要是增加try...catch错误捕获机制,比较简单
测试一下
七、补全reject和pending中的逻辑
js
// MyPromise.js
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
} else if (this.status === REJECTED) {
// ==== 新增 ====
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
} else if (this.status === PENDING) {
// 等待
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallbacks.push(() => {
// ==== 新增 ====
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
});
this.onRejectedCallbacks.push(() => {
// ==== 新增 ====
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
});
}
})
return promise2;
}
小结:
这里pending状态push函数时,添加了一个queueMicrotask函数,我测试了一下,如果这个函数里面存在微任务的话,是可以保证微任务执行完成之后再执行后续的then函数的,至于其他的用途,我需要再慢慢研究
测试一下
八、细节完善
- then中的参数变为可选
js
// MyPromise.js
then(onFulfilled, onRejected) {
// 如果不传,就使用默认函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
......
}
- 实现 resolve 与 reject 的静态调用
js
// MyPromise.js
MyPromise {
......
// resolve 静态方法
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
if (parameter instanceof MyPromise) {
return parameter;
}
// 转成常规方式
return new MyPromise(resolve => {
resolve(parameter);
});
}
// reject 静态方法
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}
小结:
这里就是添加一些细节及静态函数,很好理解
完结撒花了🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸
总结:
前路漫漫,这只是一小部分,先告一段落。常看常新!