如何在 JavaScript 中实现锁

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5346694/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:46:35  来源:igfitidea点击:

How to implement a lock in JavaScript

javascripteventsjavascript-eventslockingdeadlock

提问by smartcaveman

How could something equivalent to lockin C# be implemented in JavaScript?

如何lock在 JavaScript 中实现与 C# 中等效的东西?

So, to explain what I'm thinking a simple use case is:

所以,为了解释我在想什么,一个简单的用例是:

User clicks button B. Braises an onclick event. If Bis in event-statethe event waits for Bto be in ready-statebefore propagating. If Bis in ready-state, Bis locked and is set to event-state, then the event propagates. When the event's propagation is complete, Bis set to ready-state.

用户点击按钮BB引发一个 onclick 事件。如果B是在event-state事件等待B是在ready-state传播之前。如果B在 中ready-stateB被锁定并设置为event-state,则事件传播。当事件的传播完成时,B设置为ready-state

I could see how something close to this could be done, simply by adding and removing the class ready-statefrom the button. However, the problem is that a user can click a button twice in a row faster than the variable can be set, so this attempt at a lock will fail in some circumstances.

我可以看到如何完成与此类似的事情,只需ready-state从按钮中添加和删​​除类即可。但是,问题在于用户可以比设置变量的速度更快地连续单击按钮两次,因此在某些情况下,这种锁定尝试将失败。

Does anyone know how to implement a lock that will not fail in JavaScript?

有谁知道如何在 JavaScript 中实现一个不会失败的锁?

回答by JoshRivers

Lock is a questionable idea in JS which is intended to be threadless and not needing concurrency protection. You're looking to combine calls on deferred execution. The pattern I follow for this is the use of callbacks. Something like this:

Lock 在 JS 中是一个有问题的想法,它旨在是无线程的并且不需要并发保护。您希望结合对延迟执行的调用。我遵循的模式是使用回调。像这样的东西:

var functionLock = false;
var functionCallbacks = [];
var lockingFunction = function (callback) {
    if (functionLock) {
        functionCallbacks.push(callback);
    } else {
        $.longRunning(function(response) {
             while(functionCallbacks.length){
                 var thisCallback = functionCallbacks.pop();
                 thisCallback(response);
             }
        });
    }
}

You can also implement this using DOM event listeners or a pubsub solution.

您还可以使用 DOM 事件侦听器或发布订阅解决方案来实现这一点。

回答by Mike Samuel

JavaScript is, with a very few exceptions(XMLHttpRequestonreadystatechangehandlers in some versions of Firefox) event-loop concurrent. So you needn't worry about locking in this case.

除了极少数例外XMLHttpRequestonreadystatechange某些版本的 Firefox 中的处理程序)之外,JavaScript 是event-loop concurrent。所以在这种情况下你不必担心锁定。

JavaScript has a concurrency model based on an "event loop". This model is quite different than the model in other languages like C or Java.

...

A JavaScript runtime contains a message queue, which is a list of messages to be processed. To each message is associated a function. When the stack is empty, a message is taken out of the queue and processed.The processing consists of calling the associated function (and thus creating an initial stack frame) The message processing ends when the stack becomes empty again.

...

Each message is processed completely before any other message is processed.This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it can be stopped at any point to run some other code in another thread.

A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.

JavaScript 有一个基于“事件循环”的并发模型。该模型与其他语言(如 C 或 Java)中的模型完全不同。

...

JavaScript 运行时包含一个消息队列,它是要处理的消息列表。每条消息都与一个功能相关联。当堆栈为空时,从队列中取出一条消息并进行处理。处理包括调用相关函数(从而创建初始堆栈帧)。当堆栈再次变空时,消息处理结束。

...

在处理任何其他消息之前,将完全处理每条消息。这在推理您的程序时提供了一些很好的属性,包括这样一个事实,即无论何时运行一个函数,它都不能被抢占,并且将在任何其他代码运行之前完全运行(并且可以修改该函数操作的数据)。这与 C 不同,例如,如果一个函数在一个线程中运行,它可以随时停止以在另一个线程中运行一些其他代码。

这种模型的一个缺点是,如果一条消息需要很长时间才能完成,Web 应用程序将无法处理用户交互,如单击或滚动。浏览器通过“脚本运行时间过长”对话框来缓解这种情况。要遵循的一个好做法是缩短消息处理时间,并在可能的情况下将一条消息切成几条消息。

For more links on event-loop concurrency, see E

有关事件循环并发的更多链接,请参阅E

回答by Tim Scott

I've had success mutex-promise.

我已经成功mutex-promise

I agree with other answers that you might not need locking in your case. But it's not true that one never needs locking in Javascript. You need mutual exclusivity when accessing external resources that do not handle concurrency.

我同意您可能不需要锁定您的情况的其他答案。但是,人们永远不需要在 Javascript 中锁定是不正确的。访问不处理并发的外部资源时,您需要互斥。

回答by James Westgate

Locks are a concept required in a multi-threaded system. Even with worker threads, messages are sent by value between workers so that locking is unnecessary.

锁是多线程系统中需要的一个概念。即使使用工作线程,消息也是在工作人员之间按值发送的,因此不需要锁定。

I suspect you need to just set a semaphore (flagging system) between your buttons.

我怀疑您只需要在按钮之间设置一个信号量(标记系统)。

回答by Ravia

Why don't you disable the button and enable it after you finish the event?

为什么不禁用按钮并在完成事件后启用它?

<input type="button" id="xx" onclick="checkEnableSubmit('true');yourFunction();">

<script type="text/javascript">

function checkEnableSubmit(status) {  
  document.getElementById("xx").disabled = status;
}

function yourFunction(){

//add your functionality

checkEnableSubmit('false');
}

</script>

Happy coding !!!

快乐编码!!!

回答by Kadir Can

Some addition to JoshRiver's answer according to my case;

根据我的情况,对 JoshRiver 的回答有一些补充;

var functionCallbacks = [];
    var functionLock = false;
    var getData = function (url, callback) {
                   if (functionLock) {
                        functionCallbacks.push(callback);
                   } else {
                       functionLock = true;
                       functionCallbacks.push(callback);
                        $.getJSON(url, function (data) {
                            while (functionCallbacks.length) {
                                var thisCallback = functionCallbacks.pop();
                                thisCallback(data);
                            }
                            functionLock = false;
                        });
                    }
                };

// Usage
getData("api/orders",function(data){
    barChart(data);
});
getData("api/orders",function(data){
  lineChart(data);
});

There will be just one api call and these two function will consume same result.

只有一个 api 调用,这两个函数将消耗相同的结果。