likes
comments
collection
share

来来来,给你一个方案,帮助你监听多个组件渲染完成的时机

作者站长头像
站长
· 阅读数 13

前言

最近入职了新公司,进来之后就一直在开发低代码平台的内容,一直在各个模块中反复横跳。

但是最近遇到一个比较棘手的问题 ———— 我需要知道什么时候这些模块都初始化结束了。

问题是:模块的初始化是由组件自身完成,假如我的低代码平台中配置了很多模块,我并不知道什么时候这些模块都初始化完成了,里面也有各种各样的特殊逻辑,太分散了,没办法直接监听什么时候模块都初始化结束

经过半个上午的思考我得到一个不错的解决方案。

思路

  1. 在状态中维护了两个 Map: promiseMappromiseAllMap ,用于存储模块对应的 Promise 对象。
  • promiseMap 的结构为 { [key: string]: { [targetId: string | number]: { resolve, reject, promise } } } ,其中 key 是模块的键, targetId 是模块的唯一标识符, resolvereject 是 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}
  1. 在初始化时,通过 initPromiseMap 方法为 promiseMappromiseAllMap 设置指定的 key。
initPromiseMap (key) {
    this.__promiseMap[key] = {}
    this.__promiseAllMap[key] = {}
},
  1. 在使用过程中,首先需要获取对应模块的列表,并为每个模块创建一个处于 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。
  1. 创建一个针对当前 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)
},
  1. 可以通过 targetPromiseAll 方法获取存储在 promiseAllMap 中的 allSettled Promise 对象。
  • targetPromiseAll 方法用于获取指定 key 下的 Promise.allSettled() 的结果。
 targetPromiseAll (key: string) {
    return this.__promiseAllMap[key]
}
  1. 为了监听模块是否加载完成,可以在模块渲染完成后的生命周期中读取 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
}
  1. 当所有的 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就会结束