监听多个组件渲染完成的解决方案

前言

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

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

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

但是这并难不倒我,经过半个上午的思考我得到一个不错的解决方案。

思路

我的思路是先维护两个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 的对象 ,给到promiseMapkey 和 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 PromiseallSettled 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就会结束

相关推荐
甜兒.19 分钟前
鸿蒙小技巧
前端·华为·typescript·harmonyos
她似晚风般温柔7893 小时前
Uniapp + Vue3 + Vite +Uview + Pinia 分商家实现购物车功能(最新附源码保姆级)
开发语言·javascript·uni-app
Jiaberrr4 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy4 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白4 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、4 小时前
Web Worker 简单使用
前端
web_learning_3214 小时前
信息收集常用指令
前端·搜索引擎
Ylucius4 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
tabzzz4 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百5 小时前
Vuex详解
前端·javascript·vue.js