2010-02-12 58 views
33

我有一個名爲save()的函數,該函數收集頁面上的所有輸入,並執行AJAX調用服務器以保存用戶工作的狀態。Javascript中的線程安全?

保存()當用戶點擊保存按鈕,或者執行這就要求我們在服務器上的最新狀態。其他一些行動目前稱爲(生成例如頁面文件)。

我正在增加自動保存用戶工作的功能。首先,我想阻止AutoSave和用戶生成的保存同時運行。所以我們下面的代碼(我切割的大部分代碼,這是不是1:1,但應該足以跨越的想法):

var isSaving=false; 
var timeoutId; 
var timeoutInterval=300000; 
function save(showMsg) 
{ 
    //Don't save if we are already saving. 
    if (isSaving) 
    { 
    return; 
    } 
    isSaving=true; 
    //disables the autoSave timer so if we are saving via some other method 
    //we won't kick off the timer. 
    disableAutoSave(); 

    if (showMsg) { //show a saving popup} 
    params=CollectParams(); 
    PerformCallBack(params,endSave,endSaveError); 

} 
function endSave() 
{ 
    isSaving=false; 
    //hides popup if it's visible 

    //Turns auto saving back on so we save x milliseconds after the last save. 
    enableAutoSave(); 

} 
function endSaveError() 
{ 
    alert("Ooops"); 
    endSave(); 
} 
function enableAutoSave() 
{ 
    timeoutId=setTimeOut(function(){save(false);},timeoutInterval); 
} 
function disableAutoSave() 
{ 
    cancelTimeOut(timeoutId); 
} 

我的問題是,如果這個代碼是安全的?主流瀏覽器是否只允許一個線程執行一次?

我想過是不是會更糟糕,爲用戶點擊保存,並沒有得到響應,因爲我們是自動保存(我知道如何修改代碼來處理這一點)。任何人在這裏看到其他問題?

+0

引用如果用戶單擊保存定時器將不火,直到保存功能完成? – JoshBerke 2010-02-12 17:11:25

+1

如果我正確理解你,你問用戶是否啓動了保存,setTimeout啓動保存是否會輸入相同的方法。答案是否定的,它們不會同時在save()方法中。 – 2010-02-12 17:24:27

+0

因爲這些信息有些過時,我們該怎麼做? JavaScript現在有多個線程:https://developer.mozilla.org/En/Using_web_workers – jmort253 2011-01-17 09:28:16

回答

42

瀏覽器中的JavaScript是單線程的。你將只能在任何時間在一個功能。功能將在下一個輸入之前完成。你可以依靠這種行爲,所以如果你在你的功能save(),你將永遠不會再次輸入它,直到當前的結束。

當你有異步服務器請求(或setTimeouts或setIntervals)時,有時會引起混淆(但仍然爲真),因爲那樣感覺你的函數是interleaved。他們不是。

在你的情況,另外兩個save()調用不會互相重疊,您自動保存和用戶保存可能會出現背到後端。

如果您只希望每隔x秒發生一次保存,您可以對保存功能執行setInterval並忘記它。我不認爲需要isSaving標誌。

我覺得你的代碼可以簡化很多:

var intervalTime = 300000; 
var intervalId = setInterval("save('my message')", intervalTime); 
function save(showMsg) 
{ 
    if (showMsg) { //show a saving popup} 
    params=CollectParams(); 
    PerformCallBack(params, endSave, endSaveError); 

    // You could even reset your interval now that you know we just saved. 
    // Of course, you'll need to know it was a successful save. 
    // Doing this will prevent the user clicking save only to have another 
    // save bump them in the face right away because an interval comes up. 
    clearInterval(intervalId); 
    intervalId = setInterval("save('my message')", intervalTime); 
} 

function endSave() 
{ 
    // no need for this method 
    alert("I'm done saving!"); 
} 

function endSaveError() 
{ 
    alert("Ooops"); 
    endSave(); 
} 
+0

是的,因爲我使用isSaving標誌在保存功能異步調用應讓我阻止其他人進入該功能,直到我結束保存已運行...對嗎? – JoshBerke 2010-02-12 17:12:59

+1

@Josh我更新了我的答案一點關於isSaving標誌。所有setTimeout都會在將來至少安排一些x毫秒的函數調用。這並不意味着它會放棄它的功能並運行你的功能 - 你的預定保存將不會與另一個功能同時運行。 – 2010-02-12 17:17:48

+0

由於save會對我們的服務器進行異步調用,因此isSaving會阻止發生保存,直到調用EndSave函數。 – JoshBerke 2010-02-12 17:32:24

2

所有主要的瀏覽器目前單線程的JavaScript執行(!只是不使用web workers,因爲一些瀏覽器都支持這種技術),所以這方法是安全的。請參閱Is JavaScript Multithreaded

+0

+1,用於包括網絡工作者在內的鏈接。 – 2010-02-12 17:19:07

+0

Web工作人員受到的支持如何?看起來像Firefox 3.5+還沒有找到顯示瀏覽器支持此功能的列表。 – JoshBerke 2010-02-12 17:33:31

+0

我不完全確定。根據約翰Resig(這裏 - http://ejohn.org/blog/web-workers/),FireFox 3.5和Safari 4(截至2009年7月29日)。他還提出構建代碼的方法,以便在受支持時使用Web工作人員(如果我閱讀的話)。更重要的是,它們似乎是HTML 5規範的一部分(http://www.whatwg.org/specs/web-workers/current-work/) – 2010-02-12 17:45:33

7

所有主流瀏覽器只支持一個javascript線程(除非您使用web workers)。

儘管XHR請求可以是異步的。但只要您禁用保存功能,直到當前的保存退貨請求,所有事情都可以正常工作。

我唯一的建議就是確保在發生自動保存時禁用保存按鈕等等,以便以某種方式指示用戶。

+0

啊,你讓我跳到網絡工作者的評論。 – 2010-02-12 17:16:40

+0

是思考如何向用戶指出這一點偉大點 – JoshBerke 2010-02-12 17:18:01

+0

如果你有一個Gmail帳戶,你會注意到他們自動保存草稿時的做法是灰掉「立即保存」按鈕並將文本更改爲「保存」然後到「已保存」。只有當用戶開始更改草稿時,「立即保存」按鈕纔會返回到可點擊的狀態。 – mikefrey 2010-02-13 01:35:37

1

我覺得你處理它的方式是最好的你的情況。通過使用標誌,您可以保證異步調用不會重疊。我不得不處理對服務器的異步調用,也使用某種標誌來防止重疊。

正如其他人已經指出的那樣,JavaScript是單線程的,但是如果您期望在往返服務器的過程中發生相同或不發生異步調用會非常棘手。

不過,有一件事是,我不認爲你實際上需要禁用自動保存。如果自動保存嘗試在用戶保存時發生,則保存方法將簡單地返回並且不會發生任何事情。另一方面,每次啓用自動保存時,都會不必要地禁用並重新啓用自動保存。我建議改爲setInterval然後忘記它。

此外,我是一個最小化全局變量的堅持者。我可能會重構你的代碼是這樣的:

var saveWork = (function() { 
    var isSaving=false; 
    var timeoutId; 
    var timeoutInterval=300000; 
    function endSave() { 
     isSaving=false; 
     //hides popup if it's visible 
    } 
    function endSaveError() { 
    alert("Ooops"); 
    endSave(); 
    } 
    function _save(showMsg) { 
    //Don't save if we are already saving. 
    if (isSaving) 
    { 
    return; 
    } 
    isSaving=true; 

    if (showMsg) { //show a saving popup} 
    params=CollectParams(); 
    PerformCallBack(params,endSave,endSaveError); 
    } 
    return { 
    save: function(showMsg) { _save(showMsg); }, 
    enableAutoSave: function() { 
     timeoutId=setInterval(function(){_save(false);},timeoutInterval); 
    }, 
    disableAutoSave: function() { 
     cancelTimeOut(timeoutId); 
    } 
    }; 
})(); 

您不必重構它這樣的,當然,但就像我說的,我想盡量減少全局。重要的是,整個事情應該在每次保存時不禁用和重新啓用自動保存的情況下運行。

編輯:忘了創建一個私有保存功能,能夠因此,基於從enableAutoSave