Javascript中的线程安全?
我有一个名为save()的函数,该函数收集页面上的所有输入,并对服务器执行AJAX调用以保存用户的工作状态 当前,当用户单击“保存”按钮或执行某些其他操作时,会调用save(),这些操作要求我们拥有服务器上的最新状态(例如,从页面生成文档) 我增加了每隔一段时间自动保存用户工作的功能。首先,我想防止自动保存和用户生成的保存同时运行。因此,我们有以下代码(我正在删减大部分代码,这不是1:1,但应该足以让大家理解这个想法): 我的问题是这个代码是否安全?主要浏览器一次只允许执行一个线程吗Javascript中的线程安全?,javascript,asp.net-ajax,concurrency,Javascript,Asp.net Ajax,Concurrency,我有一个名为save()的函数,该函数收集页面上的所有输入,并对服务器执行AJAX调用以保存用户的工作状态 当前,当用户单击“保存”按钮或执行某些其他操作时,会调用save(),这些操作要求我们拥有服务器上的最新状态(例如,从页面生成文档) 我增加了每隔一段时间自动保存用户工作的功能。首先,我想防止自动保存和用户生成的保存同时运行。因此,我们有以下代码(我正在删减大部分代码,这不是1:1,但应该足以让大家理解这个想法): 我的问题是这个代码是否安全?主要浏览器一次只允许执行一个线程吗 我的一个想
我的一个想法是,如果用户单击“保存”而没有得到响应,情况会更糟,因为我们正在自动保存(我知道如何修改代码来处理这个问题)。有人看到其他问题吗?浏览器中的JavaScript是单线程的。在任何时间点,您都只能使用一个函数。在输入下一个函数之前,函数将完成。您可以指望这种行为,因此如果您在
save()
函数中,在当前操作完成之前,您将不会再次输入它
当您有异步服务器请求(或settimeout或setinterval)时,这有时会让人困惑(但仍然是真的),因为这样感觉就像您的函数正在运行一样。他们不是
在您的情况下,虽然两个save()
调用不会相互重叠,但您的自动保存和用户保存可能会背靠背进行
如果您只想至少每x秒进行一次保存,您可以在save函数上执行setInterval并将其忘掉。我认为没有必要使用isSaving
标志
我认为您的代码可以简化很多:
var intervalTime = 300000;
var intervalId = setInterval("save('my message')", intervalTime);
function save(showMsg)
{
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params, endSave, endSaveError);
// You could even reset your interval now that you know we just saved.
// Of course, you'll need to know it was a successful save.
// Doing this will prevent the user clicking save only to have another
// save bump them in the face right away because an interval comes up.
clearInterval(intervalId);
intervalId = setInterval("save('my message')", intervalTime);
}
function endSave()
{
// no need for this method
alert("I'm done saving!");
}
function endSaveError()
{
alert("Ooops");
endSave();
}
目前所有主流浏览器都是单线程javascript执行(只是因为少数浏览器支持这种技术,所以不使用),所以这种方法是安全的
有关大量参考资料,请参见?所有主要浏览器在一个页面上只支持一个javascript线程(除非您使用) 不过,XHR请求可以是异步的。但只要您禁用保存功能,直到当前保存请求返回,一切都会正常进行
我唯一的建议是,确保您以某种方式向用户指示何时发生自动保存(禁用保存按钮等)。我觉得安全。Javascript是单线程的(除非您使用的是webworkers) 这篇文章的主题不太明确,但John Resig的这篇文章介绍了javascript线程和计时器:
我认为你的处理方式最适合你的情况。通过使用该标志,可以确保异步调用不会重叠。我还必须处理对服务器的异步调用,并使用某种标志来防止重叠 正如其他人已经指出的那样,JavaScript是单线程的,但是如果您希望在往返到服务器的过程中出现相同或不相同的情况,那么异步调用可能会很棘手 不过,有一件事是,我认为您实际上不需要禁用自动保存。如果用户在保存时尝试自动保存,则save方法将简单地返回,而不会发生任何事情。另一方面,每次激活autosave时都会不必要地禁用和重新启用autosave。我建议改为setInterval,然后忘记它 而且,我是一个坚持最小化全局变量的人。我可能会像这样重构您的代码:
var saveWork = (function() {
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function endSave() {
isSaving=false;
//hides popup if it's visible
}
function endSaveError() {
alert("Ooops");
endSave();
}
function _save(showMsg) {
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
return {
save: function(showMsg) { _save(showMsg); },
enableAutoSave: function() {
timeoutId=setInterval(function(){_save(false);},timeoutInterval);
},
disableAutoSave: function() {
cancelTimeOut(timeoutId);
}
};
})();
当然,你不必像那样重构它,但就像我说的,我喜欢最小化全局变量。重要的是,在每次保存时,整个过程都应该能够正常工作,而不会禁用或重新启用autosave
编辑:您必须创建一个私有保存函数,以便能够从enableAutoSave中引用,因此基于此,如果用户单击“保存”,计时器在保存函数完成之前不会启动?如果我理解正确,您是在问用户是否已启动了保存,setTimeout启动的保存是否会输入相同的方法。答案是否定的,它们永远不会同时出现在save()方法中。由于这些信息有些过时,我们该怎么办?JavaScript现在有多个线程:@jport-仍然没有问题。WebWorkers将脚本文件作为输入,并提供线程间通信的方法。这个问题不会受到WebWorkers的影响。是的,由于我在saving标志的save函数中有异步调用,因此我应该阻止其他人进入该函数,直到我的最终save运行为止……对吗?@Josh我更新了一点关于isSaving标志的回答。setTimeout所做的就是在将来至少安排x毫秒的函数调用。这并不意味着它将放弃正在执行的操作并运行您的函数——您的预定保存不会与另一个函数同时运行。因为save对我们的服务器进行异步调用,所以在调用EndSave函数之前,isSaving会阻止保存发生。@Josh这是一个有效的推理,因为它是异步的。你担心用户会一次又一次地按下保存按钮吗?另一个选项是禁用保存按钮,直到回调发生。当用户启动保存时,我们会弹出一个半透明面板,阻止用户执行任何操作。我更担心的是,当用户做了一些导致保存被占用的事情时,自动保存会运行。我们正在考虑如何向用户指出这一点。如果你有一个gmail帐户,你会注意到,当自动保存草稿时,他们的方法是灰显“立即保存”按钮,将文本更改为“保存”,然后再更改为“保存”。只有当用户开始更改草稿时,“立即保存”按钮才会返回到clic
var saveWork = (function() {
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function endSave() {
isSaving=false;
//hides popup if it's visible
}
function endSaveError() {
alert("Ooops");
endSave();
}
function _save(showMsg) {
//Don't save if we are already saving.
if (isSaving)
{
return;
}
isSaving=true;
if (showMsg) { //show a saving popup}
params=CollectParams();
PerformCallBack(params,endSave,endSaveError);
}
return {
save: function(showMsg) { _save(showMsg); },
enableAutoSave: function() {
timeoutId=setInterval(function(){_save(false);},timeoutInterval);
},
disableAutoSave: function() {
cancelTimeOut(timeoutId);
}
};
})();