【问题标题】:Potential race conditions when Promise used in subscriptions in Javascript / TypeScriptJavascript / TypeScript 订阅中使用 Promise 时的潜在竞争条件
【发布时间】:2021-10-22 03:27:21
【问题描述】:

我最近深入研究了 Subject/BehaviorSubject/ 等的订阅,我正在寻找与 Promises 结合使用时的 goto 方法。

给出的示例代码如下:

firebase.user.subscribe((user: any | null) => {
    fs.readFile('path/to/file')
         .then((buf: Buffer) => {
             this.modifySomeData = buf;
         });
});

我订阅了一个主题,该主题在用户登录或退出其服务时触发。每当发生这种情况时,我都会从磁盘读取文件。此readFile 事件可能可能比下一个“登录/注销”事件花费更长的时间。当然,我在 JS 和异步环境中。这意味着,我的用户代码不是多线程的,但第二个用户事件和第二个 readFile 理论上可能比第一个 readFile 更快。

  1. 第一个用户事件被触发
  2. 第一个 readFile 被执行
  3. 第二个用户事件被触发
  4. 第二个 readFile 被执行
  5. 第二个 readFile 被解析
  6. 第一个 readFile 已解析

顺序搞混了。我能想到的最愚蠢的方法是在读取文件之前创建一个uuid,并检查承诺是否仍然相同。如果不是,我会丢弃数据。

有没有更好的解决方案?

【问题讨论】:

    标签: javascript node.js typescript promise observable


    【解决方案1】:

    如果我有一个可以丢弃旧请求的过程,我通常会在范围内保留一个变量来跟踪最新请求并进行比较,类似于您的 UUID 想法:

    let lastRead: Promise<Buffer> | null = null;
    
    firebase.user.subscribe((user: any | null) => {
        const read = lastRead = fs.readFile('path/to/file');
        read.then((buf: Buffer) => {
            if (read != lastRead)
                return;
    
            this.modifySomeData = buf;
        });
    });
    

    在这种特定情况下,readFile 还支持中止信号。所以你也可以中止最后一个请求;不过,您仍然需要跟踪它。

    【讨论】:

    • 谢谢!我接受了它作为答案,因为它非常符合我对uuid 的方向,除非你使用不同的表达方式。感谢您的确认!
    【解决方案2】:

    第一种方法是查看您的事件生成逻辑是否可以处理等待事件处理。例如,您可以使用承诺等待事件或生成另一个事件,例如doneReadFile,然后才发送下一个事件。通常,通用(分布式)环境并非如此。

    如果事件生成不关心处理事件需要多长时间,您仍然可以使用上述方法,但在下一个事件处理程序(登录/注销)中检查中间事件doneReadFile。这可以通过实现某种pollingbusy-wait/sleep 来实现

    【讨论】:

    • 谢谢!这帮助很大!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    相关资源
    最近更新 更多