-
21-09-2019 - |
题
我有一个网络应用程序,其中有一个不断倒计时的计时器。同时,客户端频繁地与服务器检查以查看定时器是否添加了更多时间。代码看起来像这样:
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;
这样你就不会失去你担心的那一秒。
尽管如此,最坏的情况下你也只会损失一秒钟。