You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript多函数await异步实例时如何避免重复执行load方法

JavaScript异步单例加载优雅实现方案

问题核心原因

原有逻辑重复触发异步加载的根源是:第一次调用load()时,代码先执行await等待Promise完成,才会把结果赋值给instance变量。在Promise处于pending状态的窗口期(比如例子中t1到t6之间),后续调用load()判断时instance还是undefined,就会重复创建新的Promise任务,导致多次执行异步逻辑。

缓存Promise本身的写法已经可以解决重复执行的问题,只是每个业务方法都要手动写const instance = await this.load()存在重复代码,可以通过以下方案优化。


基础修复写法(零额外依赖)

如果不需要做架构层面的封装,只需要把原来的load方法调整赋值顺序即可解决重复执行问题:

let instancePromise;
async load() {
  // 只要Promise已经创建过,不管是pending还是resolved状态都直接返回
  if (instancePromise) return instancePromise;
  // 先把Promise实例存下来,再等待结果,避免窗口期重复创建
  instancePromise = new Promise(resolve => setTimeout(() => {
    resolve([]);
  }, 5000));
  return instancePromise;
}

调整后执行逻辑完全符合预期:

  • t1调用load()创建Promise,t6时resolve
  • t3调用load()直接返回已存在的pending状态Promise,不会创建新的定时器
  • foo和bar都会在t6同时拿到加载完成的实例,不会等到t8
  • t6之后调用load()会直接返回已resolve的Promise,无额外异步开销

类场景优雅封装方案

针对类业务场景,可以通过以下方式减少重复代码:

方案1:统一实例访问入口

用私有字段缓存加载状态,通过getter统一暴露实例访问入口,不需要单独维护load方法的调用:

export default class {
  // 私有字段缓存加载Promise和最终实例,避免外部篡改
  #modulePromise = null;
  #instance = null;

  constructor() {
    // 实例化时自动触发预加载,注释掉即可实现懒加载(第一次访问实例时才加载)
    this.#loadModule();
  }

  #loadModule() {
    if (!this.#modulePromise) {
      this.#modulePromise = import('amodule')
        .then(({ default: Ctor }) => new Ctor())
        .then(instance => {
          this.#instance = instance;
          return instance;
        });
    }
    return this.#modulePromise;
  }

  // 统一访问入口,自动处理加载等待
  get instance() {
    // 实例加载完成后直接返回同步值,无异步开销
    if (this.#instance) return this.#instance;
    return this.#loadModule();
  }

  async foo() {
    const instance = await this.instance;
    // 直接使用instance写业务逻辑
  }

  async bar({ params }) {
    const instance = await this.instance;
    // 直接使用instance写业务逻辑
  }
}

方案2:装饰器自动注入实例

如果项目支持装饰器语法(TS或配置了Babel装饰器插件),可以把实例等待逻辑抽成通用装饰器,业务方法完全不需要写加载相关代码:

// 通用实例等待装饰器
function InjectInstance(target, context) {
  return async function(...args) {
    const instance = await this.instance;
    return target.call(this, instance, ...args);
  }
}

export default class {
  #modulePromise = null;
  #instance = null;

  #loadModule() {
    if (!this.#modulePromise) {
      this.#modulePromise = import('amodule')
        .then(({ default: Ctor }) => new Ctor())
        .then(instance => {
          this.#instance = instance;
          return instance;
        });
    }
    return this.#modulePromise;
  }

  get instance() {
    if (this.#instance) return this.#instance;
    return this.#loadModule();
  }

  @InjectInstance
  foo(instance) {
    // 直接使用注入的instance,不需要写任何加载逻辑
  }

  @InjectInstance
  bar(instance, { params }) {
    // 第一个参数为自动注入的实例,后续参数为业务入参
  }
}

方案3:Proxy自动代理(适配多方法场景)

如果类中需要使用实例的方法很多,不想逐个加装饰器,可以通过Proxy自动代理所有方法调用,统一注入实例:

export default class {
  #modulePromise = null;
  #instance = null;

  constructor() {
    this.#loadModule();
    // 返回代理对象,自动拦截方法调用
    return new Proxy(this, {
      get(target, prop, receiver) {
        const value = Reflect.get(target, prop, receiver);
        // 非方法属性、私有方法不做代理
        if (typeof value !== 'function' || prop.toString().startsWith('#')) {
          return value;
        }
        // 业务方法自动等待实例加载完成后注入
        return async function(...args) {
          const instance = await target.instance;
          return value.call(target, instance, ...args);
        }
      }
    })
  }

  #loadModule() {
    if (!this.#modulePromise) {
      this.#modulePromise = import('amodule')
        .then(({ default: Ctor }) => new Ctor())
        .then(instance => {
          this.#instance = instance;
          return instance;
        });
    }
    return this.#modulePromise;
  }

  get instance() {
    if (this.#instance) return this.#instance;
    return this.#loadModule();
  }

  async foo(instance) {
    // 直接使用instance
  }

  async bar(instance, { params }) {
    // 直接使用instance
  }
}

所有方案的核心逻辑一致:缓存pending状态的Promise对象,而不是等Promise resolve完成后再缓存结果。只要第一次触发加载时就把Promise存下来,后续所有调用都共享同一个Promise,自然不会出现重复执行的问题。

内容的提问来源于stack exchange,提问作者gremo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:54:24