为什么 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”)。如果不是,您可以通过删除 asyncawait.

使状态转换器成为常规函数