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") 。
有什么想法吗?
JavaScript 没有锁定语义,因为 JS 不是多线程语言。多个线程只能在完全不同的上下文中同时运行——例如。 HTML5 Worker 线程,或者 JavaScriptCore API 上下文对象的多个实例(我假设 SpiderMonkey 有类似的概念)。它们不能具有共享状态,因此本质上所有执行都是原子的。
好的,因为您现在已经提供了一些代码,我假设您有类似的内容:
External Process:
<JSObject>.isRunning = true;
doSomething()
<JSObject>.done()
或者一些这样的(使用适当的 API)。在这种情况下,如果 JS 在 js 对象的上下文中执行(这就是 JavaScriptCore 所做的),我希望 JS 引擎会阻止,否则您可能需要在 js 执行周围设置手动锁定。
您使用什么引擎来完成这一切?我问这个问题是因为根据您的描述,听起来您正在使用该语言提供的 C/C++ API 从非 JS 语言的辅助线程设置标志,并且大多数 JS 引擎假定通过 API 进行的任何状态操作将发生在单个线程上,通常是所有执行发生在同一个线程上。
首先,虽然 javaScript 确实是单线程的,但 javaScript 应用程序并不需要任何序列化机制。
一个简单的例子是,在向服务器发出 Ajax 请求期间,提交按钮应该淡出一段设定的时间。当异步 Ajax 请求成功完成时,按钮原来所在的位置应该会出现一条消息。
虽然能够取消按钮的淡出并简单地将其样式设置为“display: none”会很好,但一旦 Ajax 请求完成,这在 jQuery 中是不可能的。此外,解决方案可以使用事件来同步两个同时进行的活动,但这对于一个简单的问题来说本质上是矫枉过正。
一个低技术解决方案是轮询锁定,当淡出完成时,它会被解锁,但“服务器完成”消息不会显示,直到执行 $.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 之前在本讨论中建议的思路。
也许你可以实现一个基本的整数信号量,只需将变量添加到 DOM 中并锁定/解锁它,并确保你的函数不断检查它,否则超时 =)
如果您使用的是 Mootools 等框架,您可以尝试使用 onComplete 等事件来处理应用程序的流程。
我不太确定问题到底在问什么,但请在这里查看我的信号量对象:https://github.com/agamemnus/semaphore.js.
我有填充选择列表的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];
}
我也遇到了同样的问题,这是我解决的方法。它适用于两个并发进程。如果您有三个或更多进程,则两个进程可能一起启动。
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
。
我使用
async-mutex
包来解决 Expo SQLite 在 iOS 中不阻止 BEGIN TRANSACTION 的问题。您创建互斥体并将关键部分包装在 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;
}
});
问题:a 函数以异步方式(并行)调用,但r 函数以同步方式调用。 类似这个
a->b->
a->. b->
a->. b->
const arr = [1, 2, 3, 4, 5];
let S = 1;
const a = (val) => {
return new Promise((res, _rej) => setTimeout(() => {
console.log("value is processed by a ", val);
res(val);
}, 4000));
};
const r = (val) => {
setTimeout(() => {
console.log("value is processed by b ", val);
S = 1;
}, 100);
};
const condition1 = () => S==1
async function waitForCondition(condition) {
while (!condition()) {
await new Promise(resolve => setTimeout(resolve, 1000)); // wait for 1 second
}
S=0;
}
arr.forEach((v) => {
a(v).then(async () => {
await waitForCondition(condition1);
r(v);
});
})
S 代表锁定。 1表示锁已释放,0表示已锁定。 现在,当第一个值到达 waitForCondition 函数时,它将等待直到锁被释放,并每秒检查一次是否释放锁。第一个值的执行线程(如果不是 lock )将转到 r 函数并锁定功能。第二个值的执行线程将等待直到锁被释放,因此第二个值的执行将坚持waitForCondition语句并且不会调用r。
以此为基础,你可以尝试重建你的逻辑。