如何等待异步设置的值?解决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
相关产品推荐
相关产品推荐

