使用Promise.all从Redis赋值后局部变量未更新问题求助
问题排查
核心原因
- 循环变量未做块级作用域绑定:所有
for循环里的i都没有声明,属于隐式全局变量,所有异步回调引用的都是同一个i。当Promise的回调执行时,同步循环已经跑完,i的值已经变成了4,你实际赋值和打印的是自动创建的全局变量b4,原来的b1/b2/b3从未被修改过。你在then回调里看到的打印结果实际是b4的三次更新输出,并不是b1/b2/b3的值,属于认知偏差。 - 次要隐患:Redis的写入操作
client.set是异步API,你没有等待写入完成就发起读取请求,高延迟场景下可能出现读不到数据的问题。 - 不推荐使用
eval:既存在安全风险,也容易引发作用域相关的意外问题,完全可以用数组/对象存储变量替代。
修复方案
最优写法(移除eval,优化结构)
// 用数组存储三个结果,不需要单独声明b1/b2/b3 let b = [[], [], []] // 把redis的回调API转为Promise风格,方便用async/await const { promisify } = require('util') const setRedis = promisify(client.set).bind(client) const getRedis = promisify(client.get).bind(client) async function start(){ // 等待所有Redis写入完成再读数据 let data = [] const writePromises = [] for(let i=1; i<=3; i++){ data.push(i) writePromises.push(setRedis('a'+i, JSON.stringify(data))) } await Promise.all(writePromises) // 并发读取所有Redis数据 const readPromises = [] for(let i=1; i<=3; i++){ readPromises.push(getRedis('a'+i)) } const results = await Promise.all(readPromises) // 把结果赋值到b数组 results.forEach((res, index) => { b[index] = JSON.parse(res) console.log(b[index]) }) // 这里直接用b数组即可,索引0对应原b1,1对应原b2,2对应原b3 console.log('所有结果:', b) } start()
保留b1/b2/b3变量的兼容写法
只需要给所有循环的i加上let声明,绑定块级作用域即可:
let b1 = [] let b2 = [] let b3 = [] const { promisify } = require('util') const setRedis = promisify(client.set).bind(client) const getRedis = promisify(client.get).bind(client) async function start(){ let data = [] const writePromises = [] // 给i加let声明 for(let i=1; i<=3; i++){ data.push(i) writePromises.push(setRedis('a'+i, JSON.stringify(data))) } await Promise.all(writePromises) let arrayX = [] // 给i加let声明 for(let i=1; i<=3; i++){ arrayX.push(getRedis('a'+i).then(result => { eval('b'+i + '=' + result) console.log(eval('b'+ i)) })) } await Promise.all(arrayX).then(()=>{ // 给i加let声明 for(let i=1; i<=3; i++){ console.log(eval('b'+ i)) } }) } start()
内容的提问来源于stack exchange,提问作者Pranjal Pratim Hazarika
相关产品推荐
相关产品推荐

