Reputation: 188
so i am implementing in javascript this feature: when the document is opened by the user its data gets saved every 10 seconds and when the user closes it is saved one more time. It works seemingly well. Here is the implementation:
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);
});
}
I was wondering if my approach is correct and if it can lead to some possible race condition in extreme circumstances such as:
when the savedata() instance called from onDocumentClose() is after the if(!saveagain) step, the savedata() instance called from the previous timer of the setTimeout() is before that step, so it gets to be called once more. Can this or anything more weird happen ?
Thanks in advance
EDIT:
After considering T.J. Crowder's and Bengi's comments I finalized the code as such:
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);
});
}
I think I have handled every occasion now. I think the beingsaved solution is equal to the atomic countr that T.J Crowder suggested.
EDIT2: Hm, I'm not sure i solved it because there may be a case when if(beingsaved) gets evaluated by the setTimeout call JUST before the beingsaved is set to true by the onDocumentClose call. Can this happen ?
Upvotes: 2
Views: 689
Reputation: 664195
No. Javascript is single-threaded (apart from innovations like WebWorkers and Co, which need to communicate on a event-based interface).
So once your synchronous execution is started (the global script, the timeout, the ajax event handler), it runs and can't be stopped. Everything else (new events, 0ms-timeouts, etc.) will be scheduled afterwards.
Your script contains 2 asynchronous scenarios: the timeout and the ajax callback. After you started the loop onDocumentOpen, it just goes like that:
...and so on.
Your onDocumentClose can only execute during a wait period, when no other execution runs. You'd exspect the following:
But you didn't secure the case when documentClose happens during the ajax request:
So it will always come to an end. If one of the events would fire during something executes, there just would be no "nothing happens" in between - but it will get executed one after the other. Even if the timeout should end during the execution of the ajax callback and before it is cleared, it will be de-scheduled when it's getting cleared after its end:
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);
Upvotes: 1
Reputation: 1073978
I assume your save operation is asynchronous, as all good ajax operations should be. If so, you'll want to put some kind of guarding condition around it so that the two save triggers don't overlap. Or, of course, allow them to overlap but handle it server side (perhaps with a sequence number or timestamp), where it ignores an earlier save if a later one has already been committed.
Upvotes: 2