如何在 JavaScript 中实现锁
Posted
技术标签:
【中文标题】如何在 JavaScript 中实现锁【英文标题】:How to implement a lock in JavaScript 【发布时间】:2011-07-17 19:55:47 【问题描述】:如何在 javascript 中实现与 C# 中的 lock
等效的东西?
所以,为了解释我的想法,一个简单的用例是:
用户点击按钮B
。 B
引发 onclick 事件。如果B
在event-state
中,则事件在传播之前等待B
在ready-state
中。如果B
在ready-state
中,B
被锁定并设置为event-state
,则事件传播。事件传播完成后,B
设置为 ready-state
。
我可以看到如何完成类似的事情,只需从按钮中添加和删除类ready-state
。但是,问题是用户连续两次单击按钮的速度比设置变量的速度要快,因此这种锁定尝试在某些情况下会失败。
有谁知道如何在 JavaScript 中实现一个不会失败的锁?
【问题讨论】:
将不胜感激考虑并行 JS 执行(即像在 IE9 中)的答案。 @smart 所以你想暂时暂停当前的事件传播,直到前一个事件的传播完成。我不认为那可以做到。您可以这样做:丢弃该事件,然后在前一个事件完成传播时触发另一个事件。 @OrangeDog - 我只听说 IE9 尝试使用专用内核进行编译,没有关于并行执行,你能引用一个来源吗? @Brandon - 正如您所建议的,这可能意味着与渲染器平行,而不是与自身平行。 相关:***.com/questions/6266868/… 【参考方案1】:锁在 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 解决方案来实现这一点。
【讨论】:
能否请您提供参考文档来证明您的声明“JS 旨在实现无线程且不需要并发保护”?我想了解更多相关信息。 +1 - 鉴于 Node.js(JavaScript Web 服务器)是为利用 JavaScript 的单线程和回调机制而构建的,我同意您不必担心锁定属性将没有竞争条件。 $.longRunning 来自哪个库?它不在(当前)jQuery 中。 什么时候设置functionLock? “旨在实现无线程且不需要并发的 JS”是可疑的。 JS 不使用抢占式并发,但允许您使用协作并发(基于回调或异步/等待)。在使用这些时,您肯定会遇到竞争条件,并且可能希望使用互斥抽象来避免它们。【参考方案2】:JavaScript 与 exceptions(在某些版本的 Firefox 中为 XMLHttpRequest
onreadystatechange
处理程序)event-loop concurrent 是一样的。所以在这种情况下你不必担心锁定。
JavaScript 有一个基于“事件循环”的并发模型。该模型与 C 或 Java 等其他语言中的模型完全不同。
...
JavaScript 运行时包含一个消息队列,它是要处理的消息列表。每条消息都关联一个功能。当堆栈为空时,从队列中取出一条消息并进行处理。 处理包括调用相关函数(并因此创建一个初始堆栈帧)。当堆栈再次变空时,消息处理结束。
...
每条消息都在处理任何其他消息之前被完全处理。这在推理您的程序时提供了一些很好的属性,包括这样一个事实:每当一个函数运行时,它不能被抢占并且会在任何其他代码运行之前完全运行(并且可以修改函数操作的数据)。 这与 C 不同,例如,如果一个函数在一个线程中运行,它可以随时停止以在另一个线程中运行其他代码。
此模型的一个缺点是,如果一条消息需要很长时间才能完成,Web 应用程序将无法处理用户交互,例如单击或滚动。浏览器通过“脚本运行时间过长”对话框来缓解这种情况。 一个好的做法是缩短消息处理时间,并在可能的情况下将一条消息缩减为多条消息。
有关事件循环并发的更多链接,请参阅E
【讨论】:
并且在javascript中使用Web Workers(non UI threads),每个对象clones在发送到另一个工作线程(线程)或UI线程之前,然后对象在不同的线程中有不同的实例,每个线程都有自己的事件循环,最后我同意迈克的回答作为一个有用的答案。谢谢迈克。【参考方案3】:我已经成功了mutex-promise。
我同意您可能不需要锁定您的情况的其他答案。但是,永远不需要锁定 Javascript 是不正确的。在访问不处理并发的外部资源时,您需要互斥。
【讨论】:
感谢非常需要的东西。【参考方案4】:锁是多线程系统所需的概念。即使使用工作线程,消息也是在工作线程之间按值发送的,因此不需要锁定。
我怀疑您只需要在按钮之间设置一个信号量(标记系统)。
【讨论】:
你有任何用 JavaScript 实现的“信号量/标记系统”的示例或资源吗? PLenty 就在这里,即***.com/questions/4194346/… James 我在您发布的链接中没有看到任何信号量示例。我需要阻止对外部服务器的调用,因为它们每秒只允许一个请求,所以我需要一个标记示例来阻止调用,直到我的 for next 循环内经过的时间过去,而不跳过每个循环的请求。 嗨@克劳斯。有两件事浮现在脑海中,根据您的堆栈,您可以只使用像 Axios 这样的库,它似乎支持速率限制插件,或者您可以创建一个基于域的速率限制请求的网络工作者。 感谢您的回复。我通过在处理结束时结合 setInterval 调用 click 事件来解决它。将间隔设置为 1000 毫秒我现在遵守主机服务器对请求的限制,并且在返回响应之前它不再退出 Ajax 调用。【参考方案5】:为什么不禁用按钮并在完成事件后启用它?
<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>
编码愉快!!!
【讨论】:
【参考方案6】:根据我的情况,对 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 调用,这两个函数将消耗相同的结果。
【讨论】:
【参考方案7】:锁在 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
)
【讨论】:
【参考方案8】:这是一个简单的锁机制,通过闭包实现
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
【讨论】:
以上是关于如何在 JavaScript 中实现锁的主要内容,如果未能解决你的问题,请参考以下文章