2010-05-10 56 views
6

我在頁面加載後動態添加一些<script>標籤到head元素。我知道這些腳本是異步加載的,但是我能期望它們按照它們添加的順序被解析嗎?應該訂購動態腳本嗎?

我在Firefox中看到預期的行爲,但在Safari或Chrome中看不到。看着Chrome開發者工具和Firebug的文檔時,都表現出以下 -

<html> 
    <head> 
    ... 
    <script type="text/javascript" src="A.js"></script> 
    <script type="text/javascript" src="B.js"></script> 
    </head> 
    ... 
</html> 

但是看資源裝載視圖,鉻似乎解析無論是從服務器首先返回,而螢火蟲總是加載它們爲了添加腳本標籤,即使B首先從服務器返回。

我應該期望Chrome/Safari以指定的順序解析文件嗎?在OS X上使用Chrome 5.0.375.29測試版10.6.3

EDIT(10年10月5日):當我說的解析,我的意思是執行 - 可以看到積極的分析有很多好處 - THX rikh

EDIT( 11/5/10):好的,我按照下面的juandopazo的說法進行了一項測試。但是我添加了一些東西的組合,包括

  1. 直接用javascript將腳本元素添加到頭部。 (測試A - > D)
  2. 使用jquery的append()方法將腳本元素添加到頭部。 (測試E→H)
  3. 用jquery的getScript()方法'加載'腳本。 (測試I - > L)

我還嘗試了腳本標記上'異步'和'延遲'屬性的所有組合。

您可以在此訪問測試 - http://dyn-script-load.appspot.com/,並查看源代碼以瞭解其工作原理。加載的腳本只需調用update()函數。

首先要注意的是,只有上述第一種和第三種方法並行操作 - 第二種方法順序執行請求。你可以看到這個位置圖 -

圖片1 - 請求生命週期的圖表
Request lifecycle Graph http://dyn-script-load.appspot.com/images/dynScriptGraph.png

這也是有趣的是,jQuery的追加()方法也阻止getScript加入()調用 - 你可以看到,沒有任何的它們執行直到所有的append()調用完成,然後它們全部並行運行。最後需要注意的是,jQuery append()方法顯然會在腳本執行完成後從文檔頭中移除腳本標記。只有第一種方法會在文檔中留下腳本標記。

鉻結果

結果是Chrome瀏覽器始終執行的第一個腳本返回,不管測試。這意味着除了jQuery append()方法外,所有測試都失敗了。

圖片2 - Chrome 5.0.375。29測試結果
Chrome Results http://dyn-script-load.appspot.com/images/chromeDynScript.png

火狐結果

Firefox上,然而,它看起來是,如果使用第一種方法,和異步爲假(即,未設置),則腳本將可靠地執行在訂購。

圖片3 - FF 3.6.3結果
FF Results http://dyn-script-load.appspot.com/images/ffDynScript.png

注意的Safari似乎給不同的結果以相同的方式如鉻,這是有意義的。

另外,我只對緩慢的腳本500ms的延遲,只是爲了保持開始 - >完成時間了。您可能需要刷新幾次才能看到Chrome和Safari在所有方面都會失敗。

在我看來,如果不這樣做的方法,我們不採取的​​檢索並行數據的能力優勢,沒有理由我們不應該(火狐所示)。

+0

我已經提出了這個bug在Chrome的問題寄存器 - http://code.google.com/p/chromium/issues/detail? id = 46109 – hawkett 2010-06-08 22:45:51

回答

2

對不起回答我自己的問題,但它已經有一段時間了,我們確實想出了一個解決方案。我們想到的是以json對象中包含的文本方式同時加載javascript,然後在所有加載的eval()以正確的順序執行它們時使用eval()。併發負載加上有序執行。根據你的用例,你可能不需要json。粗略地說,這裏是一些代碼,顯示了我們所做的事情 -

// 'requests' is an array of url's to javascript resources 
var loadCounter = requests.length; 
var results = {}; 

for(var i = 0; i < requests.length; i++) { 
    $.getJSON(requests[i], function(result) { 
     results[result.id] = result; 
     ... 
     if(--loadCounter == 0) finish(); 
    }); 
} 

function finish() { 
    // This is not ordered - modify the algorithm to reflect the order you want 
    for(var resultId in results) eval(results[resultId].jsString); 
} 
0

據我所知,他們的意思是執行在他們出現在文件中的順序。某些瀏覽器可能能夠按順序執行一些解析,但它們仍然必須以正確的順序執行。

+0

這也是我的理解。 – hawkett 2010-05-10 16:36:21

0

不,你不能指望直到都裝載了所有瀏覽器都將推遲兩個腳本執行(**尤其是當你動態地添加它們)。

如果你想在B.js執行代碼A.js只加載後,那麼你最好的選擇是一個onload回調添加到A.js,設置一個變量,一個又一個B.js檢查,看看是否該變量已定,那麼它在B.js中執行必要的功能(並且如果A.js尚未加載,它會啓動一個定時器,該定時器會定期檢查直到它已加載)。

0

下載順序和執行順序不是一回事。在您的頁面中,即使首先下載B.js,瀏覽器的引擎也會等待A.js繼續處理頁面。

腳本肯定處理,不僅在他們在文檔中出現的順序,而且在他們出現的地方。

試想一下,如果它不會是這樣的,如果你使用jQuery的小腳本下載和jQuery庫之前處理會有很多錯誤。

此外,當你在一個js文件中做了一個「文件撰寫」,它出現在腳本已申報。您不能訪問腳本聲明後出現的DOM對象。

這就是爲什麼有建議將腳本放在頁面的最底部,以防止它們過早執行,並減少頁面的「感知加載時間」,因爲瀏覽器的渲染引擎一旦停止腳本被處理。

邁克

編輯:如果他們是用JavaScript動態添加,我覺得他們是在他們被及時添加順序進行處理。

+0

偉大的這是我期待的 - 所以如果我看到腳本*不是按它們出現在文檔中的順序執行的(這也是它們在時間上添加的順序),那麼這將是一個錯誤在WebKit中? (1)以正確順序顯示腳本的實際結構(2)開發工具中的資源加載視圖顯示第一個接收到的第二個腳本(3)控制檯日誌顯示腳本添加的順序(及時);(4)控制檯日誌顯示第一個正在執行的腳本 – hawkett 2010-05-10 16:59:57

+0

也許在A.js的最後一行,放置一行以包含B.js通過一個「document.write」? – 2010-05-10 18:15:05

+0

主要目標是並行加載腳本,但按添加的順序執行。這適用於Firefox,但不適用於Chrome/Safari。我可以在測試中使用第二種方法(請參閱主文章中的編輯)以確保所有瀏覽器中的正確執行順序,但不是併發的。我想,而不是採取你建議的方法來獲得序列加載(並因此執行),我會去與jQuery.append(),因爲腳本本身不需要額外的代碼。也就是說,我現在有串行加載 - 我正在尋找併發負載解決方案來提高性能 - 歡呼聲。 – hawkett 2010-05-12 09:06:59

0

您可以加載b.jsa.js是100%肯定......雖然我想明確的回答這個問題我自己,特別是與腳本的同步AJAX加載。

+0

這絕對有效 - 我正在擺脫這樣做,以獲得同時加載腳本的性能優勢:) – hawkett 2010-05-10 16:55:44

0

我正在研究一個小庫,它像YUI 3一樣動態地加載模塊。我在這裏創建了一個小測試,加載兩個腳本,只是將內容插入到div中。一個是常用的JS文件,另一個是等待3秒執行的PHP文件。

http://www.juandopazo.com.ar/tests/asyn-script-test.html

正如你所看到的,當他們完成加載腳本執行的,而不是在你追加他們的DOM,在每個瀏覽器的順序。

+0

我看了一下測試的DOM結構,並注意到了一些事情 - 首先腳本是在主體中而不是頭部,第二個腳本是第一個列出的 - 所以如果訂購被批准,你會期望腳本2執行第一個 - 很好的測試,但是當腳本標籤是爲了。對吧 – hawkett 2010-05-10 17:06:09

+0

對。對不起,我忘了把它們放在頭上。它們的順序錯誤,因爲我使用insertBefore()而不是appendChild()。我改變了它,結果是一樣的。 你應該看看寫一些隊列。請求調用來自全局對象的方法的不同文件中的模塊。看看YUI3是如何工作的。 使用代碼的腳本通常如下所示: YUI()。use('module1','module2',function(Y){ // do something }); 並且每個模塊啓動: YUI()。add('module1',function(Y){ // add your stuff to Y }); – juandopazo 2010-05-10 23:08:31

+0

謝謝你 - 我沒有足夠的聲譽來標記對不起。試圖找出爲什麼我看到firefox在我的場景中按照預期行事,但不在你的場景中。看起來我可能誤解了我的情況。 – hawkett 2010-05-11 08:16:13