我有一个网络应用程序,其中有一个不断倒计时的计时器。同时,客户端频繁地与服务器检查以查看定时器是否添加了更多时间。代码看起来像这样:

function tick() {
    // This function is called once every second
    time -= 1;
    redisplay(time);
};
function update(newtime) {
    // This function is called whenever the ajax request
    // to the server yields a new time
    time = newtime;
};

当然,它比这更复杂一些,但您可以看到固有的竞争条件。如果更新和勾选函数都试图修改怎么办 time 同时?

坦率地说,我对 javascript 的了解还不够多,无法理解如何处理此类并发问题:有没有一种简单的方法可以做到这一点,或者如果没有,有人可以向我指出可以了解更多信息的资源吗?

谢谢。

有帮助吗?

解决方案

你没有竞争条件,因为 Javascript 不会同时执行。

每次从异步操作(AJAX、 setTimeout, 等),该回调必须先完成执行,然后才能调用另一个异步操作的另一个回调。因此,如果 update() 正在运行,没有其他 Javascript 正在运行。一次 update() 完成后,可以触发来自异步操作的其他回调(例如, tick())。有趣的是,这也是原因 setTimeout 及其同类不能保证在达到超时的精确时刻执行:其他一些 javascript 可能会阻止回调的执行。

查看 http://ejohn.org/blog/how-javascript-timers-work/ 有关这一切如何运作的一些有用信息。

其他提示

JavaScript 是单线程的。没有竞争条件。javascript 中没有办法处理这两行 time = newtime;time -= 1; 在执行过程中重叠。事实上,这两个功能是保证不重叠的。其中一个将运行,然后另一个将运行。

我错了:这并不能解决问题!(代码后有说明)

NEWTIME = false;
function tick() {
    // This function is called once every second
    if (NEWTIME) {
        time = NEWTIME;
        NEWTIME = false;
    }
    time -= 1;
    redisplay(time);
};
function update(newtime) {
    // This function is called whenever the ajax request
    // to the server yields a new time
    NEWTIME = newtime;
};

这个错误解决方案的问题在于,这样做只需将竞争条件问题从变量中移走 time 到变量 NEWTIME.

想想看:的执行 tick 到达并执行该行 time = NEWTIME; 现在,在继续之前,更新被调用并且 NEWTIME 得到一个值 X. 。现在tick执行继续执行 NEWTIME = false;. 。这样你就失去了 X 的价值 NEWTIME 等等 update() 调用的效果!

该问题需要一些信号量。在前一个完成后发送 ajax 时,我经常这样做。你的情况有点相似;)

尝试类似的事情。它应该忽略所有与 ajax 回调冲突的减少时间的尝试。

window.TimeKeeper = new function(){
this.time=0; //initial value, whatever
this.isopen=true;

this.decrement = function(){ 
 if(this.isopen){
  this.time--;
  redisplay(this.time);
  }
 }
this.set = function(val){
 if(this.isopen){
  this.isopen=false;
  this.time=val;
  this.isopen=true;
  } else {
  //another AJAX callback is modifying time. 
   //You can enqueue current value and put it later
  }
 }

}

function tick() {
    TimeKeeper.decrement();

};
function update(newtime) {
    TimeKeeper.set(newtime);
};

另一件事 - setTimeout 的工作方式就像一个新线程,我希望浏览器能够同步内存访问,因此检查值在递减之前是否没有增长可能就足够了。但上述方案更加灵活、安全。

还有一个小技巧 - 避免过于频繁地使用 AJAX 进行查询 - 这可能会导致额外的问题 - 例如请求返回的顺序与发送的顺序不同,并且每分钟使用过多的 ajax 会导致 Firefox 内存使用量增加;)

只要您在当前时间上再增加几秒,就应该没问题。

而不是做 time = newtime; 尝试 time += newtime; 这样你就不会失去你担心的那一秒。

尽管如此,最坏的情况下你也只会损失一秒钟。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top