如何在JavaScript中实现锁定


83

如何lock在JavaScript中实现与C#等效的功能?

因此,解释一下我在想一个简单的用例是:

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

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

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


1
考虑到并行JS执行(如IE9)的答案将不胜感激。
OrangeDog 2011年

@smart因此,您想暂时暂停当前事件的传播,直到完成先前事件的传播为止。我不认为那是可以做到的。您可以这样做:丢弃该事件,然后在上一个事件传播完毕后触发另一个事件。
森那维达斯

@OrangeDog-我只听说过IE9尝试使用专用内核进行编译,而与并行执行无关,您能引用一个源代码吗?
布兰登

1
@Brandon-如您所建议,这可能意味着与渲染器平行,而不是与自身平行。
OrangeDog 2011年

Answers:


84

锁在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);
             }
        });
    }
}

您还可以使用DOM事件侦听器或pubsub解决方案来实现此目的。


18
您能否提供参考文档来证明您的陈述“ JS旨在实现无线程且不需要并发保护”?我想了解更多有关此的内容。
smartcaveman 2011年

2
+1-鉴于Node.js(JavaScript Web服务器)是为了利用JavaScript的单线程和回调机制而构建的,我同意您不必担心锁定属性,因为不会出现竞争条件。
Fenton

4
$ .longRunning从哪个库开始?它不在(当前)jQuery中。
Quantum7年

3
什么时候应该设置functionLock?
艾尔顿·加西亚·桑塔纳

9
“旨在无线程且不需要并发的JS”令人怀疑。JS不使用抢占式并发,但允许您使用协作式并发(基于回调或基于异步/等待)。使用这些条件时,您肯定可以具有竞争条件,并且可能希望使用互斥体抽象来避免它们。
ysdx

32

除了少数例外XMLHttpRequest onreadystatechange某些版本的Firefox中的处理程序),JavaScript是事件循环并发的。因此,您无需担心这种情况下的锁定。

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

...

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

...

在处理任何其他消息之前,将完全处理每个消息。在推理程序时,这提供了一些不错的属性,包括以下事实:每当函数运行时,它都不会被抢占并且将在其他任何代码运行之前完全运行(并且可以修改函数操作的数据)。例如,这与C不同,在C中,如果函数在线程中运行,则可以在任何时候将其停止以在另一个线程中运行其他代码。

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

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


并且使用javascript中的Web Workers(非UI线程),每个对象在发送给另一个worker(线程)或UI线程之前都会被克隆,然后该对象在不同线程中具有不同的实例,并且每个线程都拥有事件循环,最后我同意Mike的回答一个有用的答案。谢谢迈克。
Ehsan Mohammadi

10

我已经取得了互斥诺言的成功。

我同意其他答案,即您可能不需要锁定案件。但是,并非永远不需要锁定Java语言是不正确的。访问不处理并发的外部资源时,您需要互斥。


谢谢非常需要的东西。
Dmitrij Polyanin

6

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

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


1
您是否有使用JavaScript实现的“信号/标记系统”的此类示例或资源?
smartcaveman 2011年


詹姆斯,我在您发布的链接中看不到任何信号量示例。我需要阻止对外部服务器的调用,因为它们每秒仅允许一个请求,因此我需要一个标记示例,该示例将阻止调用,直到经过我的下一个循环所用的时间,而不会跳过每个循环的请求。
克劳斯

嗨,克劳斯。我想到了两件事,根据您的堆栈,您可以使用像Axios这样的库,该库似乎支持速率限制插件,或者您可以创建一个根据域对速率进行限制请求的Webworker。
詹姆斯·韦斯特盖特

感谢您的回复。我通过与setInterval结合在处理结束时调用click事件来解决此问题。将间隔设置为1000毫秒,现在我服从主机服务器对请求的限制,并且在返回响应之前,它不再退出Ajax调用。
克劳斯

2

完成活动后,为什么不禁用按钮并启用它呢?

<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>

快乐编码!


0

根据我的情况,为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);
});

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


0

锁在JS中仍然有用。以我的经验,我只需要使用锁来防止垃圾邮件点击进行AJAX调用的元素。如果您为AJAX调用设置了加载程序,则不需要这样做(以及单击后禁用按钮)。但是这两种方式都是我用来锁定的:

var LOCK_INDEX = [];
function LockCallback(key, action, manual) {
    if (LOCK_INDEX[key])
        return;
    LOCK_INDEX[key] = true;
    action(function () { delete LOCK_INDEX[key] });
    if (!manual)
        delete LOCK_INDEX[key];
}

用法:

手动解锁(通常用于XHR)

LockCallback('someKey',(delCallback) => { 
    //do stuff
    delCallback(); //Unlock method
}, true)

自动解锁

LockCallback('someKey',() => { 
    //do stuff
})

0

这是一个简单的锁机制,通过闭包实现

const createLock = () => {

    let lockStatus = false

    const release = () => {
        lockStatus = false
    }

    const acuire = () => {
        if (lockStatus == true)
            return false
        lockStatus = true
        return true
    }
    
    return {
        lockStatus: lockStatus, 
        acuire: acuire,
        release: release,
    }
}

lock = createLock() // create a lock
lock.acuire() // acuired a lock

if (lock.acuire()){
  console.log("Was able to acuire");
} else {
  console.log("Was not to acuire"); // This will execute
}

lock.release() // now the lock is released

if(lock.acuire()){
  console.log("Was able to acuire"); // This will execute
} else {
  console.log("Was not to acuire"); 
}

lock.release() // Hey don't forget to release

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.