为什么 JS redis 写入成功后会循环多次?
Why is JS redis looping more times after succesful write?
我有以下读写redis状态的逻辑
export async function updateRedis() {
let stateName = 'stateName'
try {
let isSuccess = false
while (!isSuccess) {
try {
await redis
.watch(stateName, function (err) {
if (err) {
logger.error(`Error in watch state: ${err}`)
}
redis.get(stateName, function (err, result) {
if (err) {
logger.error(`Error in get state: ${err}`)
}
let state = JSON.parse(result)
// do some processing
redis.multi()
.set(stateName, JSON.stringify(state))
.exec(function (err, result) {
if (err) {
logger.error(`Error in set state: ${err}`)
}
if (result != null) {
isSuccess = true
}
})
console.log(`isSuccess for ${stateName} `, isSuccess)
})
})
} catch (e) {
logger.error(`Error: ${e}`)
}
}
} catch (e) {
logger.error(`Error: ${e}`)
}
return Promise.resolve(true)
}
这将打印出来
"isSuccess for stateName false"
"isSuccess for stateName true"
"isSuccess for stateName true"
所以flag变为true后,会继续进行更多的循环。有时不止一次。
我是不是做错了什么?
您不能将同步循环 (while (!isSuccess) { ... }
) 与异步函数 (redis.watch(stateName, function (err) { ... })
) 混合使用。
你也可以不await
callback-based异步函数。函数 必须 return 一个可等待的承诺。由于 node-redis 在您不向其方法传递回调时给予您承诺,关键是不要这样做 (redis.watch(stateName, function (err) { ... })
→ redis.watch(stateName)
)。
您的方法需要重做。
让我们做一个函数封装一个redis transaction with optimistic locking。它需要一个连接对象、一个键和一个 value-transforming 函数,它 return 是 .set()
操作的结果:
const redisTransaction = async (client, key, transformer) => {
// https://github.com/redis/node-redis/blob/master/docs/isolated-execution.md
return client.executeIsolated(async isolatedClient => {
await isolatedClient.watch(key);
const val = await isolatedClient.get(key);
return isolatedClient.multi()
.set(key, await transformer.call(isolatedClient, val))
.exec();
});
};
现在您可以 await
这个功能,因为它 return 是一个承诺。这意味着我们可以创建一个简单的无限循环,在成功的情况下立即退出(通过 return
),或者无限期地重试。
export async function updateRedis(key, transformer) {
while (true) {
try {
return await redisTransaction(redis, key, transformer);
} catch (err) {
logger.error(`Error for state ${key}: ${err}`);
}
}
}
一个转换器函数接受一个值,return一个新值。在其中,this
关键字引用交易中的 isolatedClient
,如果您的转换取决于来自该客户端的其他值,这可能很有用。
const result = await updateRedis('stateName', async function (val) {
const state = JSON.parse(val);
const newState = await modifyStateSomehow(state);
return JSON.stringify(newState);
});
modifyStateSomehow()
本身可以是一个异步函数(即“promise-returning”)。如果不是,您可以通过删除 async
和 await
.
使状态转换器成为常规函数
我有以下读写redis状态的逻辑
export async function updateRedis() {
let stateName = 'stateName'
try {
let isSuccess = false
while (!isSuccess) {
try {
await redis
.watch(stateName, function (err) {
if (err) {
logger.error(`Error in watch state: ${err}`)
}
redis.get(stateName, function (err, result) {
if (err) {
logger.error(`Error in get state: ${err}`)
}
let state = JSON.parse(result)
// do some processing
redis.multi()
.set(stateName, JSON.stringify(state))
.exec(function (err, result) {
if (err) {
logger.error(`Error in set state: ${err}`)
}
if (result != null) {
isSuccess = true
}
})
console.log(`isSuccess for ${stateName} `, isSuccess)
})
})
} catch (e) {
logger.error(`Error: ${e}`)
}
}
} catch (e) {
logger.error(`Error: ${e}`)
}
return Promise.resolve(true)
}
这将打印出来
"isSuccess for stateName false"
"isSuccess for stateName true"
"isSuccess for stateName true"
所以flag变为true后,会继续进行更多的循环。有时不止一次。
我是不是做错了什么?
您不能将同步循环 (while (!isSuccess) { ... }
) 与异步函数 (redis.watch(stateName, function (err) { ... })
) 混合使用。
你也可以不await
callback-based异步函数。函数 必须 return 一个可等待的承诺。由于 node-redis 在您不向其方法传递回调时给予您承诺,关键是不要这样做 (redis.watch(stateName, function (err) { ... })
→ redis.watch(stateName)
)。
您的方法需要重做。
让我们做一个函数封装一个redis transaction with optimistic locking。它需要一个连接对象、一个键和一个 value-transforming 函数,它 return 是 .set()
操作的结果:
const redisTransaction = async (client, key, transformer) => {
// https://github.com/redis/node-redis/blob/master/docs/isolated-execution.md
return client.executeIsolated(async isolatedClient => {
await isolatedClient.watch(key);
const val = await isolatedClient.get(key);
return isolatedClient.multi()
.set(key, await transformer.call(isolatedClient, val))
.exec();
});
};
现在您可以 await
这个功能,因为它 return 是一个承诺。这意味着我们可以创建一个简单的无限循环,在成功的情况下立即退出(通过 return
),或者无限期地重试。
export async function updateRedis(key, transformer) {
while (true) {
try {
return await redisTransaction(redis, key, transformer);
} catch (err) {
logger.error(`Error for state ${key}: ${err}`);
}
}
}
一个转换器函数接受一个值,return一个新值。在其中,this
关键字引用交易中的 isolatedClient
,如果您的转换取决于来自该客户端的其他值,这可能很有用。
const result = await updateRedis('stateName', async function (val) {
const state = JSON.parse(val);
const newState = await modifyStateSomehow(state);
return JSON.stringify(newState);
});
modifyStateSomehow()
本身可以是一个异步函数(即“promise-returning”)。如果不是,您可以通过删除 async
和 await
.