来来来,给你一个方案,帮助你监听多个组件渲染完成的时机
前言
最近入职了新公司,进来之后就一直在开发低代码平台的内容,一直在各个模块中反复横跳。
但是最近遇到一个比较棘手的问题 ———— 我需要知道什么时候这些模块都初始化结束了。
问题是:模块的初始化是由组件自身完成,假如我的低代码平台中配置了很多模块,我并不知道什么时候这些模块都初始化完成了,里面也有各种各样的特殊逻辑,太分散了,没办法直接监听什么时候模块都初始化结束
经过半个上午的思考我得到一个不错的解决方案。
思路
- 在状态中维护了两个 Map:
promiseMap
和promiseAllMap
,用于存储模块对应的 Promise 对象。
promiseMap
的结构为{ [key: string]: { [targetId: string | number]: { resolve, reject, promise } } }
,其中key
是模块的键,targetId
是模块的唯一标识符,resolve
和reject
是 Promise 的解决和拒绝函数,promise
是 Promise 对象本身。promiseAllMap
的结构为{ [key: string]: any }
,用于存储每个 key 下的 Promise.allSettled() 的结果。
__promiseMap: {
} as {[key: string]: {
[targetId: string | number]: {
resolve
reject
promise
}
}},
__promiseAllMap: {
} as {[key: string]: any}
- 在初始化时,通过
initPromiseMap
方法为promiseMap
和promiseAllMap
设置指定的 key。
initPromiseMap (key) {
this.__promiseMap[key] = {}
this.__promiseAllMap[key] = {}
},
- 在使用过程中,首先需要获取对应模块的列表,并为每个模块创建一个处于 pending 状态的 Promise 对象,并将其 resolve 和 reject 函数暂存起来。然后,将包含 resolve、reject 和 promise 的对象添加到
promiseMap
中,使用 key 和 id 进行映射。
setPromise
方法用于设置指定 key 和 id 的 Promise 对象。- 如果
promiseMap
中的指定 key 不存在,则先创建一个空对象。 - 创建一个新的 Promise 对象,并将其 resolve 和 reject 函数暂存起来。
- 将包含 resolve、reject 和 promise 的对象添加到
promiseMap
中的指定 key 和 id 下。
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。
launchPromiseAll
方法用于启动指定 key 下所有 Promise 的并行执行,并将结果存储在promiseAllMap
中。- 首先,获取指定 key 下的所有 Promise 对象,并将它们组成一个 promiseList 数组。
- 使用 Promise.allSettled() 方法对 promiseList 进行处理,并将结果存储在
promiseAllMap
中的指定 key 下。
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
方法获取存储在promiseAllMap
中的 allSettled Promise 对象。
targetPromiseAll
方法用于获取指定 key 下的 Promise.allSettled() 的结果。
targetPromiseAll (key: string) {
return this.__promiseAllMap[key]
}
- 为了监听模块是否加载完成,可以在模块渲染完成后的生命周期中读取
promiseMap
中对应模块的 promise 对象。这样就可以拿到一个包含 resolve 和 reject 函数的对象。调用这些函数可以更新对应 Promise 的状态。
getPromise
方法用于获取指定 key 和 id 的 Promise 对象。- 如果
promiseMap
中的指定 key 和 id 存在,则返回包含 resolve 和 reject 函数的对象。 - 如果
promiseMap
中的指定 key 和 id 不存在,则返回一个空对象,其中 resolve 和 reject 函数为空函数。
getPromise (key: string, id: string) {
return this.__promiseMap[key][id] || { resolve: () => {}, reject: () => {} }
},
- 拿到的结果实际上是一个包含resolve 和 reject函数的对象,其中 resolve 和 reject函数 是我们缓存下来的 模块对应的promise的 resolve 和 reject函数,调用之后可以更新这个 promise的状态
{
resolve: res,
reject: rej,
promise
}
- 当所有的 Promise 对象的状态都被更新后,allSettled Promise 的 promiseList 就全部确定了,回调函数就会执行,从而知道模块何时加载完成。
完整代码
注意:这里为了方便用的小菠萝,但是并不强管理,单独写个文件也行
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) {
this.__promiseMap[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]
}
}
})
使用
主文件
// 创建promiseMap,用于获取所有模块渲染标识
this.initPromiseMap('test')
for... {
// 创建当前卡片的promise
this.setPromise('test', id)
}
// 创建监听promise
this.launchPromiseAll('test')
// 监听promise回调
this.targetPromiseAll('test').then(() => {
console.log('结束')
})
组件侧
// 获取promise对象
const p = this.getPromise('test', id)
// 更新promise状态
p.resolve('xxx')
总结
主要通过注册很多pending状态的普通promise
并将其传入到promise.allSettled的大promise中,业务侧通过手动的调用__promiseMap中对象的resolve函数更新promise状态
当全部更新promise.allSettled的大promise就会结束
转载自:https://juejin.cn/post/7280097378027651127