2012-10-25 33 views
22

我剛剛瞭解到有關執行javascript的一個重要事實,以防萬一拋出錯誤。在我開始對此作出結論之前,我最好驗證一下我是否正確。如果前面的腳本失敗,JavaScript代碼段執行或不執行

鑑於包括2個腳本的HTML頁面:

<script src="script1.js" /> 
<script src="script2.js" /> 

SCRIPT1:

doSomething(); 

SCRIPT2:

doSomeOtherThing(); 

這有效地導致被處理作爲一個單元的單一腳本:

doSomething(); 
doSomeOtherThing(); 

特別是,如果doSomething引發錯誤,則執行被破壞。 'script2'永遠不會被執行。

這是我的「第1課」 - 有人可能會認爲它是一個單獨包含的文件,它不受script1的影響。 但它是。 =>見下文


「晚更新」 現在,如果我們改變SCRIPT2如下(假設我們有包括上述地方的jQuery):

$(document).ready({ doSomeOtherThing(); }); 

和SCRIPT2之前把腳本:

<script src="script2.js" /> 
<script src="script1.js" /> 

執行順序是有效還是 'doSomething的()' 後(有時)由 'doSomeOtherThing()'。

然而,在兩個「單位」被執行:

  • doSomething早在文檔的Java腳本
  • doSomeOtherThing的一部分,執行時被處理的document.ready事件被執行。

如果doSomeOtherThing拋出異常,它將不打破第二處理「單元」。

(我使用術語thread不要因爲我覺得所有的腳本通常由同一個線程執行的,或者更準確地說,這可能依賴於瀏覽器)

所以,我Lession 2:即使盡管JavaScript錯誤可能會阻止任何後續腳本執行,但它不會停止事件循環。


結論1

$(document).ready()確實在確定應在隨後的執行獨立於任何其他腳本的JavaScript代碼塊的非常出色。換句話說,如果您有一段JavaScript並且要確保即使其他腳本失敗,也要執行該腳本,請將其放在$(document).ready()之內。

這對我來說是新的,因爲如果腳本取決於文檔被完全加載,我只會使用該事件。


結論2

把它更進了一步,可能是一個很好的架構決定換一個$(document).ready()所有腳本,以確保所有的腳本都「排隊」的執行。另外,在上述第二示例中,如果script2.js被列入後script1.js如實施例1:

<script src="script1.js" /> 
<script src="script2.js" /> 

在script1.js錯誤將防止甚至正在註冊的doSomeOtherThing(),因爲$(document).ready()功能將不被執行。

但是,如果script1.js使用$(document).ready(),也不會發生這種情況:

$(document).ready(function() { doSomething(); }); 
$(document).ready(function() { doSomeOtherThing(); }); 

這兩條線會被處決。然後稍後事件循環將執行doSomething這將破壞,但doSomeOtherThing不會受到影響。

這樣做的另一個原因是,呈現頁面的線程可以儘快返回,並且可以使用事件循環來觸發代碼執行。


批判/問題:

  • 難道我錯了?
  • 是什麼原因導致需要立即執行,即不是將其包裝到事件中?
  • 它會顯着影響性能嗎?
  • 是否有另一種/更好的方式來實現相同的而不是使用文檔就緒事件?
  • 如果所有腳本只是將其代碼註冊爲事件處理程序,是否可以定義腳本的執行順序?事件處理程序是否按照它們註冊的順序執行?

期待任何有用的意見!

後期更新:

像Briguy37指出正確的,我的觀察一定是錯擺在首位。 (「我錯了 - 是的!」)。以他的簡單例子,我可以在所有主流瀏覽器甚至IE8中重現,即使script1引發錯誤,腳本2也會執行。

不過@馬塞羅的偉大的答案可以幫助病人在執行堆棧等等的概念,一些有識之士這似乎只是每兩個腳本在單獨執行堆棧執行。

+0

感謝您的所有發現。非常非常有用的閱讀+1 –

+0

@Beto - 感謝您的讚賞。不幸的是,我的觀察被證明是錯誤我現在更新了我的問題和接受的答案。隨時退休upvote。 – chiccodoro

+0

Hi @chiccodoro!我讀了你的文章作爲開始,然後我閱讀所有的答案。並不是所有的發現都是錯誤的,它有助於我更好地理解其他更技術性的答案。無論如何,謝謝! –

回答

8

你自己作爲一個腳本運行的第一個假設是不正確。即使Script1引發錯誤,Script2仍將執行。對於一個簡單的測試,實現以下文件結構:

-anyFolder 
--test.html 
--test.js 
--test2.js 

的test.html的內容:

<html> 
    <head> 
     <script type="text/javascript" src="test.js"></script> 
     <script type="text/javascript" src="test2.js"></script> 
    </head> 
</html> 

test.js的內容:

console.log('test before'); 
throw('foo'); 
console.log('test after'); 

TEST2的內容.js:

console.log('test 2'); 

Th當您打開的test.html(控制檯)電子輸出:

test before test.js:1 
Uncaught foo test.js:2 
test 2 

從這個測試中,你可以看到,test2.js仍然運行即使test.js拋出一個錯誤。但是,test.js在運行到錯誤後停止執行。

+0

這很有趣。這意味着當我在代碼中觀察完全相反的觀察結果時出錯了,或者它可能依賴於瀏覽器?我將不得不仔細觀察並再次驗證。 – chiccodoro

+0

@chiccodoro:好的,請讓我們知道你是否在某處找到不同的功能。我已經在IE9,Chrome和Firefox中嘗試過,每個都有相同的結果。 – Briguy37

+0

完成了,完全像你說的那樣:script2被執行(FF,IE9)。當我將標籤包含在身體底部時,我驗證了相同的行爲。但是對於相當複雜的頁面,我從中得出了我的結論,其中包含一個腳本,如果腳本之前包含的腳本失敗,則腳本將不會執行。奇怪... – chiccodoro

2

我不確定語法錯誤,但可以使用try {} catch(e) {}來捕獲錯誤並保持其餘代碼的運行。

不會直到結束

var json = '{"name:"John"'; // Notice the missing curly bracket } 

// This probably will throw an error, if the string is buggy 
var obj = JSON.parse(json); 

alert('This will NOT run'); 

運行將運行,直到結束

var json = '{"name:"John"'; // Notice the missing curly bracket } 

// But like this you can catch errors 
try { 
    var obj = JSON.parse(json); 
} catch (e) { 
    // Do or don' 
} 

alert('This will run'); 

UPDATE

我只是想說明如何確保代碼的其餘部分被執行以防萬一發生錯誤。

是什麼原因導致需要立即執行一段 代碼,即不是將它包裝到事件中?

表現。每一個這樣的事件都會填滿事件隊列。並不是說它會傷害很多,但它不是必要的......爲什麼以後要做一些工作,如果現在可以完成呢?例如 - 瀏覽器檢測和東西。

它會顯着影響性能嗎?

如果你每秒都在做這麼多次,那麼是的。

是否有另一種/更好的方法來實現相同的文件,而不是使用 文檔就緒事件?

是的。參見上面的例子。

如果所有腳本只有 將其代碼註冊爲事件處理程序,是否可以定義腳本的執行順序?事件處理程序 是按其註冊的順序執行的嗎?

是的,我敢肯定,他們是。

+0

謝謝。我假設任何一段代碼的潛在錯誤都未預先知道。一般來說,忽略這些錯誤會讓恕我直言不是一個好主意。我更喜歡讓它們冒泡直到它們被報告到瀏覽器的JavaScript控制檯中。 – chiccodoro

+0

雖然語法錯誤是一個不同的故事。我猜他們不會影響任何其他後續的腳本文件。 – chiccodoro

+0

@chiccodoro你可以通過'console.log(e)'將錯誤傳遞給控制檯。但是這種「嘗試 - 抓住」是如何去做的。不要向我傾訴,jQuery也是這樣做的!閱讀來源 - 你會看到。 – Taai

13

JS處理錯誤的方式取決於JS處理腳本的方式。它與線程沒有任何關係(或很少)。因此,你必須首先考慮JS如何通過你的代碼工作。

首先JS將會連續讀入每個腳本文件/塊(此時您的代碼只被視爲文本)。

比JS開始解釋該文本塊並將其編譯爲可執行代碼。如果發現語法錯誤,JS將停止編譯並繼續執行下一個腳本。在這個過程中,JS將每個腳本塊/文件作爲獨立的實體處理,這就是爲什麼腳本1中的語法錯誤不一定會中斷腳本2的執行。代碼將被解釋和編譯,但此時不會執行,所以throw new Error命令不會中斷執行。在編譯完所有腳本文件/塊之後,JS通過代碼(從代碼中的第一個代碼文件/塊開始)並構建一個所謂的執行堆棧(函數a調用函數b調用函數d和c ....)並按給定的順序執行它。如果在任何時候發生處理錯誤或以編程方式拋出(throw new Error('fail')),則該堆棧的整個執行都會停止,並且JS會回到堆棧的開始位置,並開始執行下一個可能的堆棧。

這就是說,你的onload函數在script1.js中出現錯誤後仍然執行的原因,並不是因爲新線程或某事而發生的,這僅僅是因爲事件會建立一個單獨的執行堆棧,JS可以跳轉在之前的執行堆棧發生錯誤之後。

來到你的問題:有沒有使得有必要立即執行一段代碼

是什麼原因,即不包成的事件?

我建議你在你的web應用程序中根本沒有「立即」稱爲代碼。最好的做法是,入口的應用程序中的單個點被稱爲onload事件

$(document).ready(function() {App.init()}); 

內然而,這沒有任何錯誤處理或等。錯誤處理本身應該在代碼中完成,條件爲if(typeof myValue !== 'undefined')或try/catch/finally塊,您可能會遇到潛在的錯誤。這也讓你有機會在catch塊內部嘗試第二種方式,或者最終妥善處理錯誤。

如果你可以構建你的應用程序事件驅動(當然不是出於錯誤處理的原因),那麼這樣做。 JS是一種事件驅動型語言,當您編寫事件驅動代碼時,您可以充分利用它...

它會對性能產生顯着影響嗎?

一個事件驅動的方法,恕我直言,你的應用程序執行得更好,同時使它更穩定。事件驅動的代碼可以幫助你減少內部處理邏輯的數量,你只需要進入它。

是否有另一種/更好的方式來實現相同的而不是使用文檔就緒事件?

正如之前提到的:的try/catch /終於

可以的,如果所有的腳本只是註冊他們的代碼的事件處理程序腳本的執行順序來定義?事件處理程序是否按照它們註冊的順序執行?

如果您在同一對象上註冊相同的事件,訂單將被保留。

+0

謝謝,Marcello!至於「子鏈」 - 你能詳細說明一下嗎?如果前面的子鏈拋出一個異常,我該如何創建一個甚至被執行的子鏈?你是指使用「try-catch」? – chiccodoro

+0

至於性能 - 這也證實了我的想法,儘管與註冊和處理事件相比,執行一個簡單的代碼可能會增加一些額外的執行時間 - 但它可以(或至少晚些時候)反過來 – chiccodoro

+0

@chiccodoro我不得不糾正一些部分,因爲它們是誤導性的。首先它不是一個執行鏈,它是一個堆棧。其次,只有3種方法來構建「sub」或分離堆棧:setTimeout,Events和eval,它不依賴於您的代碼佈局,因爲我的答案暗示。這裏有一個關於JS堆棧的好鏈接:http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/ –