문제

I have a web application where there is a timer that is constantly counting down. Meanwhile, the client frequently checks with the server to see if more time has been added to the timer. The code looks something like this:

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;
};

It is, of course, a bit more complex than that, but you can see the inherent race condition. What if the update and the tick function are both trying to modify time at the same time?

Frankly, I don't know nearly enough javascript to understand how to deal with this sort of concurrency issue: is there an easy way to do this, or if not, can somebody point me towards resources where I can learn more?

Thank you.

도움이 되었습니까?

해결책

You don't have a race condition, because Javascript doesn't execute concurrently.

Every time a callback is fired from an async operation (AJAX, setTimeout, etc), that callback must finish executing before another callback from another async operation can be called. So if update() is running, no other Javascript is. Once update() finishes, other callbacks from async operations can be fired (for example, tick()). Interestingly enough, this is also why setTimeout and its ilk aren't guaranteed to execute at the precise moment the timeout is reached: some other javascript could be blocking the callback's execution.

Check out http://ejohn.org/blog/how-javascript-timers-work/ for some good information on how all this works.

다른 팁

Javascript is single threaded. There is no race condition. There's no way in javascript for the two lines time = newtime; and time -= 1; to overlap during execution. In fact, the two functions are guaranteed not to overlap. One of them will run and then the other will run.

I was wrong: this does not solve the issue! (explaination after the code)

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;
};

The problem with this wrong solution is that doing this way you just move the race condition issue from variable time to variable NEWTIME.

Just think this: the execution of tick reaches and executes the line time = NEWTIME; now, before continuing, update get called and NEWTIME gets a value X. Now tick execution continues executing NEWTIME = false;. This way you've lost the X value of NEWTIME and so the effect of an update() call!

The problem needs some semaphores. I do that a lot for sending ajax after previous one finishes. Your case is a bit alike ;)

Try something like that. It should ignore all attempts to decrement time that collide with ajax callback.

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);
};

One more thing - the setTimeout works like a new thread and I would expect that browsers do the synchronising mem access, so It might be enough to check if the value didn't grow before decrementing. But the above solution is more flexible and safe.

And one little tip - avoid querying with AJAX too often - it may cause extra problems - like requests coming back in different order than sent, and firefox memory usage building up a lot on too much ajax a minute ;)

As long as you add some more seconds to you current time you should be fine.

Instead of doing time = newtime; try time += newtime; This way you will not lose the second you're worried about.

Still, you are only losing a second at worst.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top