Javascript中是否存在原子测试和设置,信号量或锁定这样的事情?
我有javascript通过自定义协议调用异步后台进程(后台进程实际上在一个单独的进程中运行,与浏览器无关)。我相信我遇到了竞争状态;后台进程在我的测试和我的设置之间返回,在javascript端搞砸了。我需要一个测试和设置操作来使它成为一个真正的信号量。
以下是尝试检测后台进程并将其排队的javascript代码:
Call = function () {
var isRunning = true,
queue = [];
return {
// myPublicProperty: "something",
call: function (method) {
if (isRunning) {
console.log("Busy, pushing " + method);
queue.push(method);
} else {
isRunning = true;
objccall(method);
}
},
done: function() {
isRunning = false;
if (queue.length > 0) {
Call.call(queue.shift());
}
}
};
}();
Call是一个实现排队的单例;任何想要调用外部进程的人都会调用Call.call(“something”)。
有什么想法吗?
答案 0 :(得分:20)
JavaScript没有锁定语义,因为JS不是多线程语言。多个线程只能在完全不同的上下文中同时操作 - 例如。 HTML5工作线程,或者像JavaScriptCore API的上下文对象的多个实例(我假设SpiderMonkey有类似的概念)。它们不能具有共享状态,因此实质上所有执行都是原子的。
好的,因为您现在已经提供了一些代码,我假设您有类似的内容:
External Process:
<JSObject>.isRunning = true;
doSomething()
<JSObject>.done()
或某些此类(使用适当的API)。在这种情况下,如果JS在js对象的上下文中执行(这是JavaScriptCore会做的事情),我希望JS引擎能够阻塞,如果失败,你可能需要在js执行周围放置一个手动锁。
你用什么引擎来做这一切?我问,因为基于你的描述,听起来你正在使用该语言提供的C / C ++ API从非JS语言的辅助线程设置一个标志,并且大多数JS引擎都假设通过API进行的任何状态操作将发生在一个线程上,通常是所有执行发生的相同线程。
答案 1 :(得分:2)
也许你可以实现一个基本的整数信号量,只需将变量添加到DOM中并锁定/解锁它,并确保你的函数继续检查它,否则超时它们=)
如果您使用的是Mootools等框架,您可以尝试使用onComplete等事件来处理应用程序的流程。
答案 2 :(得分:1)
首先,虽然javaScript是单线程的,但javaScript应用程序不需要任何序列化机制。
一个简单的例子,就是当一个提交按钮淡出一段时间,在这段时间内,对服务器的Ajax请求正在运行。当异步Ajax请求成功完成时,消息应该出现在按钮的位置。
虽然能够取消按钮的淡出并简单地将其样式设置为&#34; display:none&#34;,但是一旦Ajax请求完成,这是不可能的,这是不可能的jQuery的。此外,解决方案可以使用事件来同步两个同时进行的活动,但这对于一个简单的问题来说实际上是过度的。
低技术解决方案是轮询锁定,当淡出完成时,它会被解锁,但服务器已完成&#34;在$ .post设置的成功回调执行之前,不会显示消息。
var gl_lock;
var gl_selfID;
function poll_lock(message) {
if (gl_lock === 0) {
$('#output').text(message).fadeIn(200);
window.clearInterval(gl_selfID);
}
} // end of poll_lock
function worker() {
// no one gets in or out
gl_lock = 1;
$.post(..., data,function() {
gl_selfID = window.setInterval(poll_lock, 40, data.message);
}, "json");
// end of fadeout unlock the semaphore
$('#submit-button').fadeOut(400, function() { gl_lock = 0; });
} // end of worker
最后,我认为这是更详细的答案,与先前在本次讨论中由perrohunter提出的一致。
答案 3 :(得分:1)
我不确定问题究竟是什么,但请查看我的信号量对象:https://github.com/agamemnus/semaphore.js。
答案 4 :(得分:0)
我有ajax填充选择列表的东西,我需要它被锁定所以我做了这样的事情。我认为你可以更简单地使用延迟和管道或其他东西。
var semaphore=[];
function myFunc(arg){
var dfd;
$.when(semaphore[table]).done(
function(){
dfd=myFuncInner(arg);
}
);
return dfd;
}
function myFuncInner(table){
semaphore[arg] = new $.Deferred();
...
somethingasynchronous({
semaphore[arg].resolve();
});
return semaphore[arg];
}
答案 5 :(得分:0)
我遇到了同样的问题,这是我如何解决的。对于两个并发进程,它工作正常。如果您有三个或三个以上的进程,则两个进程可能会同时启动。
var isRunning = false;
...
var id = setInterval(function(){ //loop until isRunning becomes false
if (!isRunning){
isRunning = true;
//do something synchronous or use a promise
isRunning = false;
clearInterval(id); // stop the loop here
}
, 10);
它比while循环更好,因为它解决了读取/设置isRunning
的并发/异步问题。
答案 6 :(得分:0)
我使用 async-mutex
包来解决 Expo SQLite not blocking BEGIN TRANSACTION in iOS 的问题。您创建互斥锁并将临界区包装在 runExclusive
方法
return this.mutex.runExclusive(async () => {
try {
await this.executeSqlAsync('begin immediate transaction');
const tx = new AsyncSQLTransaction(this);
const rs = await callback(tx);
await this.executeSqlAsync('commit');
return rs;
} catch (error) {
await this.executeSqlAsync('rollback');
throw error;
}
});