通过Promise实现瞬时重复加载数据优化

51 阅读2分钟

需求背景是这样的,我在开发的框架中有个枚举下拉组件,该组件会自动从服务端下载数据。当组件同时存在多个,比如表格渲染的场景,则在加载页面时候会瞬时集中重复请求。 考虑到该组件本身也有全局缓存数据的需求,同时框架的请求都是通过Promise实现,于是有了下面的解决方案

import { defineStore } from 'pinia'
import { post, CommItemVO } from '@/console/api/PubDataAPI'

export const usePubDataStore = defineStore('pub-data-store', () => {
  const cache: Map<string, CommItemVO[]> = new Map()

  /* 当同时多个发起相同的数据时,只请求一次 */

  const waitingFlag: Map<string, boolean> = new Map()

  const getSelectListData = (tag: string): Promise<CommItemVO[]> => {
    const exists = cache.get(tag)
    if (!exists) {
      const isWaiting = waitingFlag.get(tag)
      if (!isWaiting) {
        waitingFlag.set(tag, true)
        return post('api/console/select/list', { tag: tag }).then((data) => {
          cache.set(tag, data)
          waitingFlag.delete(tag)
          return Promise.resolve(data)
        })
      } else {
        return new Promise((resolve, reject) => {
          //如果发现已经有在途查询,则等待,每100毫秒看下结果有没,有就继续
          let retryCount = 0
          const timer = setInterval(() => {
            retryCount++
            const cacheList = cache.get(tag)
            if (!cacheList && retryCount > 30) {
              clearInterval(timer)
              reject()
            }
            if (cacheList) {
              clearInterval(timer)
              //返回数据
              resolve(cacheList)
            }
          }, 100)
        })
      }
    } else {
      return Promise.resolve(exists)
    }
  }

  return {
    getSelectListData,
  }
})

基本实现原理:

  1. 通过pinia实现全局缓存。
  2. 当有产生http请求的时候设置等待标志位
  3. 当其它请求数据调用并进来后,发现有在途的等待标志位,则发起一个新的Promise,这个Promise其实就是定时循环,一直等待第一个http请求完成后,提取它的返回结果,resolve给自己,本身并不真正发起http请求。
  4. 当然也有退出机制,加入第一个http请求失败了,排队等待的请求在重试30次后还未找到缓存也会退出。

我是handleRich (知乎四物番鸭咖啡),正在独立开发后台管理框架,过程中遇到的有趣问题和你们分享,欢迎加入讨论。SpringBoot、Vue3、Typescript、TDesign