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

如何等待异步设置的值?解决JS类初始化竞态问题

问题描述

预先说明

  • 已理解Promise概念
  • 初始化逻辑必须在initMethod中执行,无法移至构造函数,因为这是需要特定时机触发的钩子方法

核心问题

代码存在竞态条件:当methodA在initMethod之前被调用时,会因x为null抛出错误。

示例代码:

class Service {

  private x: string | null = null;

  initMethod() {
    this.x = 'hello';
  }

  async methodA() {
    return this.x.length;
  }
}

const service = new Service();
setTimeout(() => service.initMethod(), 1000);
service.methodA().catch(console.log);

运行报错:

TypeError: Cannot read properties of null (reading 'length')
    at Service.methodA (<anonymous>:15:19)
    at <anonymous>:20:9
    at dn (<anonymous>:16:5449)

期望实现可外部设置值的Promise(命名为SettablePromise),让methodA等待x初始化完成后再执行,示例如下:

class Service {

  private x: SettablePromise<string> = new SettablePromise();

  initMethod() {
    this.x.set('hello');
  }

  async methodA() {
    return (await this.x).length;
  }
}

const service = new Service();
setTimeout(() => service.initMethod(), 1000);
service.methodA().catch(console.log);

目前想到的方案是轮询值直到非空,但不想依赖轮询间隔,希望有更优解。

补充说明

竞态条件本质是methodA可能先于initMethod执行;实际场景中initMethod为异步方法,示例未加async是为简化展示。


解决方案

你需要的SettablePromise本质是延迟Promise,可通过封装Promise的resolve/reject方法实现,无需轮询,完全基于原生异步机制。

实现SettablePromise类

class SettablePromise<T> {
  private resolve!: (value: T | PromiseLike<T>) => void;
  private reject!: (reason?: any) => void;
  private promise: Promise<T>;

  constructor() {
    this.promise = new Promise((res, rej) => {
      this.resolve = res;
      this.reject = rej;
    });
  }

  // 设置成功值
  set(value: T | PromiseLike<T>) {
    this.resolve(value);
  }

  // 设置失败原因
  setError(reason?: any) {
    this.reject(reason);
  }

  // 支持await语法
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
  ): Promise<TResult1 | TResult2> {
    return this.promise.then(onfulfilled, onrejected);
  }

  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined
  ): Promise<T | TResult> {
    return this.promise.catch(onrejected);
  }

  finally(onfinally?: (() => void) | null | undefined): Promise<T> {
    return this.promise.finally(onfinally);
  }
}

验证使用

将该类接入你的Service后,methodA会自动等待initMethod调用x.set('hello')后再执行,彻底解决竞态问题,不会再抛出错误。

异步initMethod适配

若实际场景中initMethod为异步方法,直接在内部调用this.x.set(await 异步操作)即可兼容:

class Service {
  private x: SettablePromise<string> = new SettablePromise();

  async initMethod() {
    // 模拟异步初始化逻辑
    const result = await new Promise<string>(res => setTimeout(() => res('hello'), 1000));
    this.x.set(result);
  }

  async methodA() {
    return (await this.x).length;
  }
}

const service = new Service();
service.initMethod();
service.methodA().then(console.log); // 输出5

该方案基于Promise原生异步机制,无轮询开销,性能更优,完全匹配你对SettablePromise的需求。


内容的提问来源于stack exchange,提问作者José Alvarado Torre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:40:49