使用web3 BatchRequest循环发送请求仅生效最后一个值的问题
解决Web3 BatchRequest循环中所有请求使用最后一个值的问题
这个问题我太熟了——典型的JavaScript闭包陷阱在搞鬼!
问题原因
你在forEach循环里添加的send.request回调函数,并没有捕获到当前迭代的d的值,而是捕获了d变量的引用。当batch.execute()执行的时候,整个forEach循环已经跑完了,此时d变量指向的是数组的最后一个元素(也就是test4那项),所以所有请求都会复用这个最后的值。
修复方案
下面两种方法都能解决这个问题,选你顺手的就行:
方案1:在forEach迭代内创建块级作用域的变量副本
通过let声明一个当前迭代的变量副本,让每个回调函数捕获这个独立的副本引用:
let batch = new this.web3.BatchRequest(); const arr = [ {name: "test1", att: 100, def: 100}, {name: "test2", att: 100, def: 100}, {name: "test3", att: 100, def: 100}, {name: "test4", att: 100, def: 100}, ] arr.forEach((d) => { // 用let创建当前迭代数据的独立副本,形成块级作用域 let currentItem = d; batch.add(this.contract.methods.createCountry(currentItem.name, currentItem.att, currentItem.def, 10, this.account).send.request(this.contractObject, (err, res) => { if (err) { throw err; } else { console.log(res); } })); }); console.log(batch); batch.execute();
方案2:改用for...of循环
for...of的每次迭代都会自动创建新的块级作用域变量,天然避免闭包引用问题:
let batch = new this.web3.BatchRequest(); const arr = [ {name: "test1", att: 100, def: 100}, {name: "test2", att: 100, def: 100}, {name: "test3", att: 100, def: 100}, {name: "test4", att: 100, def: 100}, ] // 替换forEach为for...of for (const d of arr) { batch.add(this.contract.methods.createCountry(d.name, d.att, d.def, 10, this.account).send.request(this.contractObject, (err, res) => { if (err) { throw err; } else { console.log(res); } })); } console.log(batch); batch.execute();
额外说明
既然你已经通过Remix和单独调用确认了合约本身没问题,那核心问题就完全出在JavaScript的变量引用机制上。上面的两种方法都能确保每个BatchRequest里的请求使用的是当前迭代的元素值,不会再全部变成最后一项。
内容的提问来源于stack exchange,提问作者jasper
相关产品推荐
相关产品推荐

