Tengo la siguiente lógica para leer y escribir el estado de 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)}
Esto se imprimirá
"isSuccess for stateName false" "isSuccess for stateName true" "isSuccess for stateName true"Entonces, después de que el indicador cambie a verdadero, continuará durante más bucles. A veces lo hace más de una vez.
¿Estoy haciendo algo mal?
No puede mezclar un bucle síncrono ( while (!isSuccess) { ... } ) con funciones asíncronas ( redis.watch(stateName, function (err) { ... }) ).
Tampoco puede await funciones asincrónicas basadas en devolución de llamada. Una función debe devolver una promesa para estar disponible. Dado que node-redis le promete cuando no pasa devoluciones de llamada a sus métodos, la clave es no hacer eso ( redis.watch(stateName, function (err) { ... }) → redis.watch(stateName) ) .
Su enfoque necesita ser rehecho.
Hagamos una función que encapsule una transacción redis con bloqueo optimista . Toma un objeto de conexión, una clave y una función de transformación de valores, y devuelve el resultado de la operación .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(); }); }; Ahora puede await esta función, porque devuelve una promesa. Eso significa que podemos hacer un ciclo infinito simple que sale inmediatamente en caso de éxito (a través de return ), o reintenta indefinidamente.
export async function updateRedis(key, transformer) { while (true) { try { return await redisTransaction(redis, key, transformer); } catch (err) { logger.error(`Error for state ${key}: ${err}`); } } } Una función de transformador toma un valor y devuelve un nuevo valor. En su interior, la palabra clave this se refiere al cliente isolatedClient de la transacción, lo que podría ser útil si su transformación depende de otros valores de ese cliente.
const result = await updateRedis('stateName', async function (val) { const state = JSON.parse(val); const newState = await modifyStateSomehow(state); return JSON.stringify(newState); }); El modifyStateSomehow() puede ser en sí mismo una función asíncrona (es decir, "devolución de promesa"). Si no es así, puede hacer que el transformador de estado sea una función normal eliminando async y await .