【问题标题】:Is it impossible to create a reliable async singleton pattern in JavaScript?在 JavaScript 中创建可靠的异步单例模式是不可能的吗?
【发布时间】:2019-11-18 17:18:08
【问题描述】:

这是我拥有的功能:

let counter = 0;
let dbConnected = false;

async function notASingleton(params) {
    if (!dbConnected) {
        await new Promise(resolve => {
            if (Math.random() > 0.75) throw new Error();
            setTimeout((params) => {
                dbConnected = true; // assume we use params to connect to DB
                resolve();
            }, 1000);
        });
        return counter++
    }
};
// in another module that imports notASingleton
Promise.all([notASingleton(params), notASingleton(params), notASingleton(params), notASingleton(params)]);

// in another module that imports notASingleton
notASingleton(params);
notASingleton(params);
notASingleton(params);
notASingleton(params);

问题在于,显然notASinglton 中的承诺可能会同时执行,并且假设它们是并行运行的,那么它们的执行上下文将是dbConnected = false

注意:我知道我们可以引入一个新变量,例如initiatingDbConnection 而不是检查!dbConnected 而是检查!initiatingDbConnection;但是,只要 concurrent 意味着 promise 的上下文在 Promise.all 中是相同的,就不会改变任何东西。

该模式可以正确实现,例如Java 利用 JVM 的契约创建类:https://stackoverflow.com/a/16106598/12144949

然而,即使是那个 Java 实现也不能用于我需要传递变量的用例:“客户端应用程序不能传递任何参数,所以我们不能重用它。例如,有一个通用的单例客户端应用程序提供数据库服务器属性的数据库连接类。” https://www.journaldev.com/171/thread-safety-in-java-singleton-classes-with-example-code

注 2:另一个可能相关的问题:https://eslint.org/docs/rules/require-atomic-updates#rule-details

【问题讨论】:

  • JavaScript 是单线程的,所以没有竞争条件。要了解如何实现单例模式,请查看 excellent book
  • @watofundefined 那是我的理解,但 MDN 注释另有建议。也许原生级别的Promise.all 实现可能会复制每个承诺的执行上下文,然后并行运行它们
  • 我现在明白你的意思了 - 所以我对 MDN 所说的理解是,你不知道运行时是否会开始以与它们在数组中。但是仍然只有一个主线程 - 如果您想更深入地了解它的工作原理,请阅读EventLoop。我将向您发布一些示例代码,以确保您只启动一次与 DB 的连接。
  • @watofundefined 是的;这就是我想它想说的(只是措辞不好),但是关于复制变量的工作方式有一些细微差别;例如这个:exploringjs.com/es6/…
  • @Bergi 谢谢!不过,我仍然不确定 promise 调用的上下文会发生什么?它们是在同一个线程中运行,还是在不同的线程中运行?实际上,我认为我的问题不仅限于 Promise.all;如果我们在词法上一个接一个地调用多个 Promise 会发生什么:promise(); promise(); promise(); promise(); 它们中的每一个的执行上下文是如何创建的?

标签: javascript asynchronous promise singleton race-condition


【解决方案1】:

“在某些计算机上,它们可能是并行执行的,或者在某种意义上是同时执行的,而在其他计算机上,它们可能是串行执行的。”

那个 MDN 描述是垃圾。我会删除它。 Promise.all 不负责执行任何事情,并且无论如何都不能“执行”承诺。它所做的只是等待它的参数,而你是在数组中创建这些承诺的人。即使您省略了Promise.all 并且只是多次调用notASingleton(),它们也会运行(并同时打开多个连接)。

所有这些的执行上下文都是dbConnected = false

是的,但这只是因为您的dbConnected = true; 处于超时状态,并且您在此之前再次调用notASingleton()。不是因为Promise.all 做了什么特别的事情。

【讨论】:

  • 代替setTimeout,它可以是任何需要时间来处理的同步操作,例如在建立连接之前,我的代码中有Buffer.from(certificate, 'base64')。我认为的问题是,只要每个promise里面的代码并行执行,问题就会一直存在?
  • @HoumanKamali JS 中的任何代码都不会并行执行。但是,是的,一旦您执行异步操作,多个异步操作可能会同时处于活动状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-04
  • 1970-01-01
  • 2020-09-08
  • 2012-03-08
  • 2013-08-20
相关资源
最近更新 更多