【发布时间】: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