如何在 JavaScript 中实现相当于 C# 中的
lock
的功能?
因此,为了解释我的想法,一个简单的用例是:
用户单击按钮
B
。 B
引发 onclick 事件。如果 B
位于 event-state
中,则事件会在传播之前等待 B
位于 ready-state
中。如果 B
在 ready-state
中,B
被锁定并设置为 event-state
,则事件将传播。当事件传播完成时,B
设置为 ready-state
。
我可以看到如何完成类似的事情,只需从按钮中添加和删除类
ready-state
即可。然而,问题是用户连续两次单击按钮的速度比设置变量的速度快,因此这种锁定尝试在某些情况下会失败。
有谁知道如何在 JavaScript 中实现一个不会失败的锁?
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);
}
});
}
}
您还可以使用 DOM 事件侦听器或 pubsub 解决方案来实现此目的。
JavaScript 是,有极少数例外(某些版本的 Firefox 中的
XMLHttpRequest
onreadystatechange
处理程序)事件循环并发。所以在这种情况下你不必担心锁定。
JavaScript 有一个基于“事件循环”的并发模型。这个模型与其他语言(如 C 或 Java)中的模型有很大不同。
...
JavaScript 运行时包含一个消息队列,它是要处理的消息的列表。每条消息都与一个功能相关联。当堆栈为空时,从队列中取出消息并进行处理。 处理包括调用关联的函数(从而创建初始堆栈帧)。当堆栈再次变空时,消息处理结束。
...
每条消息都会在处理任何其他消息之前完全处理。这在推理程序时提供了一些很好的属性,包括每当函数运行时,它都不能被抢占,并且将在任何其他代码运行之前完全运行(并且可以修改函数操作的数据)。 这与 C 不同,例如,如果一个函数在线程中运行,则可以随时停止它以在另一个线程中运行其他代码。
此模型的一个缺点是,如果消息需要很长时间才能完成,则 Web 应用程序将无法处理单击或滚动等用户交互。浏览器通过“脚本运行时间太长”对话框来缓解这种情况。 遵循的一个好习惯是缩短消息处理时间,如果可能,将一条消息削减为多条消息。
有关事件循环并发的更多链接,请参阅 E
我已经成功了互斥承诺。
我同意其他答案,您可能不需要锁定您的案例。但在 Javascript 中并不需要锁定。访问不处理并发的外部资源时,您需要互斥。
如果它对 2022 年以后的任何人有帮助,所有主要的浏览器现在都支持Web Locks API,尽管还处于实验阶段。
引用MDN中的例子:
await do_something_without_lock();
// Request the lock.
await navigator.locks.request('my_resource', async (lock) => {
// The lock has been acquired.
await do_something_with_lock();
await do_something_else_with_lock();
// Now the lock will be released.
});
// The lock has been released.
await do_something_else_without_lock();
https://example.com
!= https://example.org:8080
),并且跨选项卡/工作人员工作。navigator.locks.query()
可以用来查看什么有锁,以及队列中有谁来获取锁mode="shared"
来实现读写锁锁是多线程系统中需要的一个概念。即使使用工作线程,消息也是按值在工作线程之间发送的,因此不需要锁定。
我怀疑你需要在按钮之间设置一个信号量(标记系统)。
这是一个简单的锁机制,通过闭包实现
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
为什么不禁用该按钮并在完成活动后启用它?
<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>
快乐编码!!!
根据我的情况对 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 调用,这两个函数将消耗相同的结果。
锁在 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
})