JS/TS异步访问同一对象的问题排查与正确实现
问题分析与修复方案
底层原因
你遇到的核心问题不是竞态条件,而是函数调用时的this指向错误:
start方法中直接调用setup(),未通过this.setup()调用,导致setup内部的this不指向Deployer实例(严格模式下为undefined,非严格模式指向全局对象)。若你观察到setup能正常添加数据,大概率是非严格模式下全局对象存在同名的txDataList,但这属于错误的隐式行为。setInterval(this.consumeTXs, 5000)将consumeTXs作为独立函数传递,此时函数内部的this指向全局对象(浏览器为window,Node.js为global),而非Deployer实例,所以访问的this.txDataList根本不是类实例中初始化的数组,自然始终为空。
正确实现方式
修复this指向
通过以下方式确保函数调用时this绑定到Deployer实例:
- 使用箭头函数包裹
consumeTXs,利用箭头函数的词法this绑定特性; - 调用
setup时必须通过this.setup(); - 可选:使用
bind显式绑定this到实例。
补充原子性处理(避免后续竞态)
处理txDataList时,建议一次性取出所有数据并清空数组,避免同一批数据被多次消费,确保操作的原子性。
完整修正代码
class Deployer { protected txDataList: Array<TXData>; protected service: ServiceType; constructor(service: ServiceType) { this.txDataList = new Array<TXData>(); this.service = service; // 注入服务实例 } async consumeTXs() { // 原子性取出所有数据并清空数组 const txs = [...this.txDataList]; this.txDataList = []; if (txs.length > 0) { // 处理txs数据 for (const tx of txs) { // do something with tx } } } setup() { this.service.on("txData", (txData) => { // push是同步操作,无需async标记 this.txDataList.push(txData); }); } start() { this.setup(); // 修正:通过this调用setup // 用箭头函数包裹,确保this指向实例 setInterval(() => this.consumeTXs(), 5000); // 也可使用bind:setInterval(this.consumeTXs.bind(this), 5000); } }
关键修改点说明
- 构造函数中注入
service实例,避免未声明属性的访问错误; setup通过this.setup()调用,确保内部this指向实例;setInterval使用箭头函数调用consumeTXs,保证consumeTXs内部的this绑定到当前Deployer实例;consumeTXs中通过拷贝数组并清空的方式,实现数据的原子性消费,避免后续可能出现的竞态问题(比如消费过程中又有新数据加入,导致重复处理);- 移除
txData事件回调中的async,因为push是同步操作,无需异步标记。
内容的提问来源于stack exchange,提问作者weaver
相关产品推荐
相关产品推荐

