Javascript中的线程安全?

Javascript中的线程安全?,javascript,asp.net-ajax,concurrency,Javascript,Asp.net Ajax,Concurrency,我有一个名为save()的函数,该函数收集页面上的所有输入,并对服务器执行AJAX调用以保存用户的工作状态 当前,当用户单击“保存”按钮或执行某些其他操作时,会调用save(),这些操作要求我们拥有服务器上的最新状态(例如,从页面生成文档) 我增加了每隔一段时间自动保存用户工作的功能。首先,我想防止自动保存和用户生成的保存同时运行。因此,我们有以下代码(我正在删减大部分代码,这不是1:1,但应该足以让大家理解这个想法): 我的问题是这个代码是否安全?主要浏览器一次只允许执行一个线程吗 我的一个想

我有一个名为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);
    }
  };
})();