前言
最近入职了新公司,进来之后就一直在开发低代码平台的内容,一直在各个模块中反复横跳。
但是最近遇到一个比较棘手的问题 ------------ 我需要知道什么时候这些模块都初始化结束了。
问题是:模块的初始化是由组件自身完成,假如我的低代码平台中配置了很多模块,我并不知道什么时候这些模块都初始化完成了,里面也有各种各样的特殊逻辑,太分散了,没办法直接监听什么时候模块都初始化结束
但是这并难不倒我,经过半个上午的思考我得到一个不错的解决方案。
思路
一
我的思路是先维护两个map: promiseMap,promiseAllMap
ts
__promiseMap: {
} as {[key: string]: {
[targetId: string | number]: {
resolve
reject
promise
}
}},
__promiseAllMap: {
} as {[key: string]: any}
二
在考虑到使用场景的多样化,初始化的时候先给 promiseMap, promiseAllMap 设置一个key
js
initPromiseMap (key) {
if (this.__promiseMap[key]) this.__promiseMap[key] = {}
if (this.__promiseAllMap[key]) this.__promiseAllMap[key] = {}
},
三
使用时,需要先拿到对应的模块列表,这些模块需要有一个标识自身的id,我们通过key 和 id给每一个模块创建一个 pending
的promise。
并且暂存这个promise的res,rej函数,并最终返回一个,包含res,rej,promise 的对象 ,给到promiseMap 由key 和 id 映射的模块。
ts
setPromise (key: string, id: string | number) {
if (!this.__promiseMap[key]) {
this.__promiseMap[key] = {}
}
// 注意:这里我暂存了promise的 resolve, reject 函数
let res
let rej
const promise = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
this.__promiseMap[key][id] = {
resolve: res,
reject: rej,
promise
}
},
这样当我们执行遍历一遍模块列表之后就可以得到一个map,里面每一个模块都有自己对应的pending状态的promise。
四
紧接着我们去创建一个针对当前key下所有pending Promise 的 allSettled Promise
ts
launchPromiseAll (key: string) {
let promiseList = []
if (Object.keys(this.__promiseMap[key]).length) {
promiseList = Object.values(this.__promiseMap[key]).map(item => {
return item.promise
})
}
this.__promiseAllMap[key] = Promise.allSettled(promiseList)
},
由于promiseList中的promise都是我们创建的pending状态的promise
所以this.__promiseAllMap[key] 也是一个pending的 Promise
五
到这一步我们就可以拿到这个 存储起来的 allSettled Promise,并设置其回调,但是由于默认是pending的因此不会直接执行。
ts
targetPromiseAll (key: string) {
return this.__promiseAllMap[key]
}
六
上面的内容完成之后基础部分的操作就完成了,那我们怎么监听模块是否加载完呢?
我们知道 Promise.allSettled 需要里面所有的Promise都结束才会到下一步。
然后里面的promise都是我们从 this.__promiseMap[key] 中拿出来的 pending状态的Promise,
那么我们就可以在模块渲染完成之后的生命周期中 读到 this.__promiseMap[key] 中对应模块的promise对象
ts
getPromise (key: string, id: string) {
return this.__promiseMap[key][id] || { resolve: () => {}, reject: () => {} }
},
拿到的结果实际上是一个包含resolve 和 reject函数的对象,其中 resolve 和 reject函数 是我们缓存下来的 模块对应的promise的 resolve 和 reject函数,调用之后可以更新这个 promise的状态
ts
{
resolve: res,
reject: rej,
promise
}
七
当所有的promise状态都被更新之后,这个 allSettled Promise 的promiselist就全部都确定了,那么回调就会执行,我们就知道什么时候模块加载完了。
完整代码
注意:这里为了方便用的小菠萝,但是并不强管理,单独写个文件也行
ts
export const usePromiseStore = defineStore('promise', {
state () {
return {
__promiseMap: {
} as {[key: string]: {
[targetId: string | number]: {
resolve
reject
promise
}
}},
__promiseAllMap: {
} as {[key: string]: any}
}
},
actions: {
initPromiseMap (key) {
if (this.__promiseMap[key]) this.__promiseMap[key] = {}
if (this.__promiseAllMap[key]) this.__promiseAllMap[key] = {}
},
getKeys () {
return Object.keys(this.__promiseMap)
},
setPromise (key: string, id: string | number) {
if (!this.__promiseMap[key]) {
this.__promiseMap[key] = {}
}
let res
let rej
const promise = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
this.__promiseMap[key][id] = {
resolve: res,
reject: rej,
promise
}
},
getPromise (key: string, id: string) {
return this.__promiseMap[key][id] || { resolve: () => {}, reject: () => {} }
},
launchPromiseAll (key: string) {
let promiseList = []
if (Object.keys(this.__promiseMap[key]).length) {
promiseList = Object.values(this.__promiseMap[key]).map(item => {
return item.promise
})
}
this.__promiseAllMap[key] = Promise.allSettled(promiseList)
},
targetPromiseAll (key: string) {
return this.__promiseAllMap[key]
}
}
})
使用
主文件
ts
// 创建promiseMap,用于获取所有模块渲染标识
this.initPromiseMap('test')
for... {
// 创建当前卡片的promise
this.setPromise('test', id)
}
// 创建监听promise
this.launchPromiseAll('test')
// 监听promise回调
this.targetPromiseAll('test').then(() => {
console.log('结束')
})
组件侧
ts
// 获取promise对象
const p = this.getPromise('test', id)
// 更新promise状态
p.resolve('xxx')
总结
主要通过注册很多pending状态的普通promise
并将其传入到promise.allSettled的大promise中,业务侧通过手动的调用__promiseMap中对象的resolve函数更新promise状态
当全部更新promise.allSettled的大promise就会结束