2010-01-16 22 views
16

我有一個Web應用程序,有一個定時器不斷倒計時。同時,客戶端經常檢查服務器以查看是否有更多時間添加到計時器。代碼看起來像這樣:Ajax併發

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來理解如何處理這種併發問題:是否有一種簡單的方法來做到這一點,或者如果不是這樣,是否有人可以指向我可以學習的資源?

謝謝。

+0

一直想問這個問題。 – zedoo 2010-01-16 17:14:43

+1

非常好的問題! – 2010-01-16 17:22:47

+1

只是爲了清楚起見,這段代碼是在瀏覽器中運行,還是運行在類似node.js的服務器上? – 2010-01-16 18:58:01

回答

16

您沒有競爭條件,因爲Javascript不會同時執行。

每次從異步操作(AJAX,setTimeout等)觸發回調時,該回調必須在另一個異步操作的回調被調用之前完成執行。所以如果update()正在運行,沒有其他的Javascript是。一旦update()完成,可以觸發異步操作的其他回調(例如,tick())。有趣的是,這也是爲什麼setTimeout及其同類不能保證在超時達到的時刻執行:其他一些JavaScript可能會阻止回調的執行。

查看http://ejohn.org/blog/how-javascript-timers-work/瞭解所有這些工作的一些有用信息。

-2

只要你多添加幾秒鐘給你當前時間,你應該沒問題。

而不是做time = newtime;試試time += newtime;這樣你就不會失去你擔心的那一秒。

儘管如此,最糟糕的情況是你只剩下第二名。

+0

注意,newtime只是需要多少時間才能添加。 – 2010-01-16 17:22:35

+5

我不認爲這解決了競爭條件問題 – 2010-01-16 17:32:22

0

我錯了:這不能解決問題! (代碼後交代)

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。現在滴答執行繼續執行NEWTIME = false;。這樣你就失去了X的值NEWTIME,所以update()調用的效果!

+0

這正是我想要做的,這是如何解決競爭條件?你可能會失去一個週期,但在我看來,'NEWTIME'不會丟失。 – 2010-01-16 18:06:01

+0

@Justin這樣你就可以將變量'time'的競爭條件問題移動到變量'NEWTIME'。 想一想:tick的執行到達並執行'time = NEWTIME'這行,現在,在繼續之前,'update'被調用,'NEWTIME'得到一個值'X'。現在'tick'執行繼續執行'NEWTIME = false;'。 這樣你就失去了'update()'調用的效果! – 2010-01-16 18:11:38

+0

是的,但是,如果語言是並行的,你最多隻能輸出一次,而不是原來的輸出方式。 – 2010-01-18 10:44:54

-1

該問題需要一些信號量。在完成上一個操作後,我會爲此發送一個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的作品就像一個新的線程和我預料的瀏覽器做同步MEM訪問,所以它可能是足夠的,以檢查是否值不減量前增長。但上述解決方案更加靈活和安全。

和一個小竅門 - 避免過於頻繁地使用AJAX查詢 - 這可能會導致額外的問題 - 就像回來在不同的訂單要求比發送和Firefox的內存使用情況建立太多阿賈克斯一分鐘了很多;)

+0

謝謝。儘管減量失敗會發生什麼?時間不會重新顯示。在這種情況下,您可以將重新顯示的呼叫移動到if之外,但對於更一般的情況,是否有一種無阻塞的方式來等待並再次嘗試? 此外,我需要相當頻繁地檢查服務器的更新,目前意味着我每秒要做一次ajax調用。如果我需要經常更新,有沒有比ajax更好的方法呢?當事件發生時,服務器無法向客戶端發送通知,是嗎? – So8res 2010-01-16 19:26:43

+0

我現在沒有時間深入檢查,但這對我來說似乎不是一個好的解決方案... – 2010-01-16 19:34:12

+2

Javascript是單線程的。你不需要信號量。發生的最壞情況是減量發生(隨後是重新顯示),然後該值立即被來自update的新提供的值覆蓋。在這種情況下,時間會減少並在稍後顯示。不是問題。 – PanCrit 2010-01-16 19:38:12

8

Javascript是單線程的。沒有競賽條件。在javascript執行期間,兩行time = newtime;time -= 1;不能重疊。事實上,這兩個功能是保證不重疊。其中一個將運行,然後另一個將運行。