可能的javascript ajax调用竞争条件

时间:2012-05-21 17:23:45

标签: javascript ajax race-condition

所以我在javascript中实现了这个功能:当用户打开文档时,它的数据每10秒保存一次,当用户关闭时,它会再次保存。它看似很好。以下是实施:

var data = "blabla";
var saveagain = false;
var t1;

function onDocumentOpen() {
    saveagain = true;
    savedata();
}

function onDocumentClose() {
    saveagain = false;
    savedata();
}

function savedata() {
    if (!saveagain) clearTimeout(t1);
    SaveDataAjaxCall(data, function() {
        //data was saved
        if (saveagain) t1 = setTimeout("savedata()", 10000);
    });
}

我想知道我的方法是否正确以及是否会在极端情况下导致一些可能的竞争条件,例如:

当从onDocumentClose()调用的savedata()实例在if(!saveagain)步骤之后,从setTimeout()的前一个计时器调用的savedata()实例在该步骤之前,因此它被调用再一次。这或其他更奇怪的事情会发生吗?

提前致谢

编辑:

考虑T.J. Crowder's和Bengi的评论我最终确定了代码:

var data = "";
var saveagain = false;
var t1;
var beingsaved = false;

function onDocumentOpen() {
    saveagain = true;
    savedata();
}

function onDocumentClose() {
    saveagain = false;
    savedata();
}

function saveData() {
    if (beingsaved) {
        if (!saveagain) setTimeout(saveData, 100);
        return false;
    }
    beingsaved=true;

    if (!saveagain) clearTimeout(t1);

    data=getData();

    SaveDataAjaxCall(data, function() {
        //data was saved
        beingsaved=false;
        if (saveagain) t1 = setTimeout(saveData, 10000);
    });

}

我想我现在处理过各种场合。我认为beingsaved解决方案与T.J Crowder建议的原子国家相同。

EDIT2:嗯,我不确定我是否解决了这个问题,因为可能存在这样的情况:在beingsaved通过onDocumentClose调用设置为true之前,if(beingsaved)被setTimeout调用JUST评估。这会发生吗?

2 个答案:

答案 0 :(得分:2)

我假设您的保存操作是异步的,因为所有良好的ajax操作都应该是。如果是这样,你会想要在它周围放置一些防护条件,以便两个保存触发器不重叠。或者,当然,允许它们重叠但是处理服务器端(可能带有序列号或时间戳),如果已经提交了稍后的保存,它将忽略先前的保存。

答案 1 :(得分:1)

没有。 Javascript是单线程的(除了像WebWorkers和Co这样需要在基于事件的界面上进行通信的创新)。

因此,一旦启动同步执行(全局脚本,超时,ajax事件处理程序),它就会运行并且无法停止。其他所有事件(新事件,0ms超时等)将在之后安排。

您的脚本包含2个异步方案:超时和ajax回调。在开始循环onDocumentOpen后,它就是这样:

  1. 执行saveData:启动ajax请求
  2. 等待(直到ajax事件发生)
  3. 执行成功回调:为saveData设置新超时
  4. 等待(直到超时事件发生)
  5. 执行saveData:启动ajax请求
  6. 等待...
  7. ......等等。

    您的onDocumentClose只能在等待期间执行,此时没有其他执行运行。你会想到以下内容:

    1. ...执行saveData:启动ajax请求
    2. 没有任何反应
    3. ajax事件:执行成功回调:为saveData设置新的超时
    4. 没有任何反应
    5. documentClose事件:清除超时,启动ajax请求
    6. 没有任何反应
    7. ajax事件:执行成功回调:不再设置新的超时。端。
    8. 但是当你在ajax请求期间发生documentClose时,你没有得到保障:

      1. ...执行saveData:启动ajax请求
      2. 没有任何反应
      3. ajax事件:执行成功回调:为saveData设置新的超时
      4. 没有任何反应
      5. 超时事件:执行saveData,启动ajax请求
      6. 没有任何反应
      7. documentClose事件:清除(不存在的)超时,启动ajax请求(第二次)
      8. 没有任何反应
      9. 其中一个ajax事件:执行成功回调:不再设置新的超时。
      10. 没有任何反应
      11. 其他ajax事件:执行成功回调:不再设置新的超时。端。
      12. 所以它总会走到尽头。如果其中一个事件会在执行某些事件期间触发,那么它们之间就没有“没有任何反应” - 但它会一个接一个地执行。即使超时应该在执行ajax回调期间结束而在它被清除之前,它将在它结束后被清除时被取消调度:

        var id = setTimeout(function(){
            alert("still waiting for execution"); // never alerts
        }, 500);
        setTimeout(function(){
            alert("still waiting for execution"); // this alerts
        }, 500);
        for(var d = Date.now(); Date.now()-d < 1000; ) {
            ; // wait - the timeouts end during this heavy processing
        }
        clearTimeout(id);