接着上一篇深入了解Promise机制并使用JS实现一个Promise(一)文章接着使用js来实现一个xpromise
已实现效果
经过上一篇文章我们已近得到下面的代码,实现了promise的状态、值、以及then方法的实现。下面我们接着来实现promise中其他的方法,目前已存在的代码如下:
class XPromise {
constructor(handle) {
// 用来记录当前XPromise的状态 pending | fulfilled | rejected
this["[[PromiseState]]"] = "pending";
// 用来记录当前XPromise的值
this["[[PromiseResult]]"] = undefined;
this.resolveFnList = [];
this.rejectFnList = [];
// 使用否箭头函数规避this问题, 否则就得这里bind一下
// handle(this.#resolve.bind(this), this.#reject.bind(this))
handle(this.#resolve, this.#reject);
}
// 对标promise中的resolve
#resolve = (val) => {
this["[[PromiseState]]"] = "fulfilled";
this["[[PromiseResult]]"] = val;
// 将his.resolveFn(val)的时机放入微任务中执行
this.#observerAndExecuteFn(this.resolveFnList, val)
};
// 对标promise中的reject
#reject = (error) => {
this["[[PromiseState]]"] = "rejected";
this["[[PromiseResult]]"] = error;
// 将执行this.rejectFn(val)的时机放入微任务中执行
this.#observerAndExecuteFn(this.rejectFnList, error)
};
// 将.then函数接受的onResolve和onReject函数放在微任务中执行
#observerAndExecuteFn = (fnList, promiseResult) => {
const executeFnList = () => {
let itemFn = null;
while (fnList.length) {
itemFn = fnList.shift();
itemFn(promiseResult);
}
};
const targetNode = document.getElementsByTagName("body")[0];
const observe = new MutationObserver(executeFnList);
observe.observe(targetNode, {
attributes: true,
});
targetNode.setAttribute("x-promise", "watch");
};
/**
* xpromise实例方法
* 1. 在promise resolve/reject之后执行onResolved/onReject
* 2. 返回一个新的xpromise实例(新的实例而不是原来的xpromise实例)
* @param {*} onResolved
* @param {*} onReject
* @returns
*/
then = (onResolved, onReject) => {
// 返回一个新的xpromise实例
return new XPromise((resolve, reject) => {
// 在这里需要把当前这个promise的结果拿到
// resolveFn是需要push到函数队列中的,接受当前promise实例结果的值
const resolveFn = (val) => {
const result = onResolved && onResolved(val);
if (result instanceof XPromise) {
// result 既然是xpromise实例
// 那这个xpromise的结果就可以从.then中获取
result.then((val) => resolve(val));
} else {
resolve(result);
}
};
this.resolveFnList.push(resolveFn);
const rejectFn = (error) => {
onReject && onReject(error);
reject(error);
};
this.rejectFnList.push(rejectFn);
});
};
}
export default XPromise;
Promise实例catch方法
定义
Promise 实例的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
我们看下原生promise.catch的效果
实现
可以看到,一个是返回新的promise,另一个是可以catch住之前的onReject。而.catch实际上是promise.then(null, onReject)的一个简写,而我们在之前已经完整实现了promise.then方法,所以只需要直接return this.then(null, onReject)即可。完整代码如下:
// promise.then(null, onReject)的简写
catch = (onReject) => {
return this.then(undefined, onReject)
}
}
export default XPromise;
运行的效果如下所示:
Promise实例finnal方法
定义
finally() 方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then() 和 catch() 中各写一次的情况。
可以看到
finally有如下几个特征
- 接受一个函数作为参数,但该函数没有参数
- 没有返回
- 下一个
promise.then对象接受的是finally的上一个then的返回
实现
/**
* 本质上就是个高阶函数
* 是对this.then函数的封装,接受一个fn,
* 执行fn之后就走正常的then逻辑即可
* 可以理解为对this.then的升阶接受一个函数参数
* 高阶函数,类似HOC执行this.then之前执行fn
* @param {*} fn
* @returns
*/
finally = (fn) => {
// fn && fn(); fn 函数不能放在这里运行,不然就是同步了,应该放在下面,在resolve之后执行fn
// finally 函数就是对 this.then 函数包装了一下
// 本质还是执行this.then函数,不过在执行原本this.then函数中的onResolved和onReject之前先执行fn
return this.then(
// 这个val就是上一个then的值
val => {
// 执行传递进来的fn
fn && fn();
if (val instanceof XPromise) {
// 上一个then返回的是promise 通过then获取上一个promise的值,再resolve给下一个then即可
return val.then(v => XPromise.resolve(v))
} else {
return XPromise.resolve(val)
}
},
// 这个error就是上一个then的值
error => {
fn && fn();
if (error instanceof XPromise) {
return error.catch(v => XPromise.reject(v))
} else {
return XPromise.reject(error)
}
}
)
}
执行效果如下:
Promise.resolve 静态方法
定义
Promise.resolve() 静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;
实现
我们分析,首先Promise.resolve()函数接受一个值根据该值的类型做返回即可:
- 非promise:返回一个新的
promise实例 - promise:直接返回改promise即可 完整代码如下:
/**
* 根据参数的类别判断
* 非promise:返回一个新的`promise`实例
* promise:直接返回改promise即可
* @param {*} val
* @returns
*/
static resolve = (val) => {
if (val instanceof XPromise) {
return val
} else {
return new XPromise((resolve) => {
resolve(val)
})
}
}
代码执行效果如下:
Promise.reject静态方法
定义
Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。
实现
Promise.reject和Promise.resolve整体很像这里需要注意的是,根据MDN上的描述,我们知道返回的一定是一个新的promise对象,所以就不用对入参类型进行判断了
整体代码如下:
static reject = (error) => {
return new XPromise((undefined, reject) => {
reject(error)
})
}
代码执行效果如下所示:
Promise.race静态方法
定义
Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。我们看原生的Promise.all的效果如下:
实现
思路是接受一个promise数组,数组中的每个promise并发执行,谁最先得到结果,将结果返回resolve/reject掉即可
static race = (promiseList) => {
let execute = false
return new XPromise((resolve, reject) => {
promiseList.forEach(promiseItem => {
// 每个promiseItem都是xpromise 那就可以通过.then获取值
promiseItem.then(val => {
if (execute) return;
resolve(val)
execute = true
}, error => {
if (execute) return;
reject(error)
execute = true
})
});
})
}
代码执行效果如下:
Promise.all静态方法
定义
Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
实现
static all = (promiseList) => {
return new XPromise((resolve, reject) => {
// 1. 用于存储每个promise的结果
// 2. 返回的promise结果顺序与接入的promise顺序相同故使用new Array(promiseList.length);的方式存结果
const result = new Array(promiseList.length);
// 用于计数运行了多个个promise
let num = 0
promiseList.forEach((promiseItem, index) => {
// 每一个结果都是一个对象
result[index] = {}
promiseItem.then(val => {
// 更新该位置的promise信息
result[index]['status'] = "fulfilled"
result[index]['value'] = val
// 如果都运行完了就直接resolve
// 证明所有promise都是成功的, 整个promise状态也是成功的
num ++
if (num >= promiseList.length) resolve(result)
}, error => {
// 在promise数组中有某个promise失败了
// 整个promise状态算失败,reject失败最快的那个即可
reject(error)
})
})
})
}
代码执行效果如下:
Promise.allSettled静态方法
定义
Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组
实现
代码如下:
static allSettled = (promiseList) => {
return new XPromise((resolve, reject) => {
// 1. 用于存储每个promise的结果
// 2. 返回的promise结果顺序与接入的promise顺序相同故使用new Array(promiseList.length);的方式存结果
const result = new Array(promiseList.length);
// 用于计数运行了多个个promise
let num = 0
promiseList.forEach((promiseItem, index) => {
// 每一个结果都是一个对象
result[index] = {};
promiseItem.then((val)=> {
// 更新该位置的promise信息
result[index]['status'] = "fulfilled"
result[index]['value'] = val
// 如果都运行完了就直接resolve
num ++
if (num >= promiseList.length) resolve(result)
}, error => {
// 更新该位置的promise信息
result[index]['status'] = "rejected"
result[index]['reason'] = error
// 如果都运行完了就直接resolve
num ++
if (num >= promiseList.length) resolve(result)
})
})
})
}
代码执行效果如下:
某种意义上Promise.allSettled方法也是弥补了Promise.all方法一旦有某一个promise被拒绝了就拿不到整个结果的缺陷
Promise.any静态方法
定义
Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。
实现
static any = (promiseList) => {
return new XPromise((resolve, reject) => {
// 1. 用于存储每个promise的结果
// 2. 返回的快的promise结果先push入数组
const result = []
// 用于计数运行了多个个promise
let num = 0
// 等每个promise都处理完了,根据每个promise结果决定整个promise的结果
const dealResult = () => {
// 看能否找到一个成功的promise,先找到的也就是返回最快的promise
const successPromise = result.find(resultItem => resultItem.status === 'fulfilled')
if (successPromise) {
resolve(successPromise)
} else {
throw new Error('All promises were rejected')
}
}
promiseList.forEach((promiseItem, index) => {
// 每一个结果都是一个对象
const resultItem = {}
promiseItem.then((val)=> {
// 更新该位置的promise信息
resultItem['status'] = "fulfilled"
resultItem['value'] = val
result.push(resultItem)
// 如果都运行完了再判断整个Promise的状态
num ++
if (num >= promiseList.length) dealResult()
}, error => {
// 更新该位置的promise信息
resultItem['status'] = "rejected"
resultItem['value'] = error
result.push(resultItem)
// 如果都运行完了再判断整个Promise的状态
num ++
if (num >= promiseList.length) dealResult()
})
})
})
}
代码运行效果如下: