ホームページ  >  記事  >  ウェブフロントエンド  >  ブラウザ環境での JavaScript スクリプトの読み込みと実行の分析: 遅延と非同期の特性_JavaScript スキル

ブラウザ環境での JavaScript スクリプトの読み込みと実行の分析: 遅延と非同期の特性_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 15:20:021613ブラウズ

遅延機能と非同期機能は、多くの JavaScript 開発者にとって「よく知られているものの、馴染みのない」機能であると考えられており、文字通りの観点から見ると、これら 2 つの機能は「遅延スクリプト」です。と「非同期スクリプト」の効果です。ただし、遅延を例に挙げると、開発者は、遅延機能を備えたスクリプトの実行がいつ遅延されるのか、内部スクリプトと外部スクリプトの両方が遅延後のスクリプトをサポートできるかどうかなどの詳細に必ずしも精通しているわけではありません。遅延実行に加えて、どのような特別な機能があるかなど。この記事では、開発者がこれら 2 つの機能をよりよく習得できるように、既存の記事と MDN ドキュメントの 2 つの機能の説明を組み合わせて、遅延と非同期のより包括的な調査と概要を実施します。

1 はじめに

ブラウザ環境での JavaScript スクリプトの読み込みと実行の分析: コード実行シーケンス」では、JavaScript コードの実行により、ページの解析とレンダリング、および他のリソースのダウンロードがブロックされると述べました。もちろん、JavaScript はシングルスレッド言語であるため、通常の状況では、ページ内の JavaScript コードは上から下の順序でしか実行できません。ブラウザ環境でのJavaScriptスクリプトの読み込みと実行 「実行シーケンス」で分析したように、document.writeでスクリプトを入力したり、ダイナミックスクリプト技術でスクリプトを導入したりする場合など、JavaScriptコードの実行順序が異なる場合があります。必ずしも上から下への厳密な順序に従っているわけではありません。遅延や非同期も、いわゆる「異常な状況」です。

実際の開発では、JavaScript の実行がブロックされているとよく言われますが、通常最も懸念されるブロック、およびユーザー エクスペリエンスに最も影響を与えるブロックは次の側面です。


[1] ページの解析とレンダリングのブロック


[2] 作成したページ初期化スクリプト (通常、DOMContentLoaded イベントをリッスンするようにバインドされたスクリプト) は、ユーザー操作に最も関連するコードを作成するため、最初に実行するスクリプトです。ここです。)


[3] ページ上の外部リソース (画像など) のダウンロードをブロック


時間のかかるスクリプト操作があり、このスクリプトが上記の 3 つの場所をブロックする場合、この Web ページのパフォーマンスまたはユーザー エクスペリエンスは非常に低下します。


遅延と非同期の 2 つの機能の本来の目的は、ページ エクスペリエンスに対するブロックの影響を解決または軽減することです。これら 2 つの機能を主に次の側面から分析してみましょう。

[1]遅延スクリプトまたは非同期スクリプトの実行時間はいつですか?ページブロックについてはどうですか?

[2] 内部スクリプトと外部スクリプトの両方で、遅延または非同期実装が可能ですか?

[3]ブラウザはこれら 2 つの機能をどの程度サポートしていますか?関連するバグはありますか?

[4] これら 2 つの機能を使用するスクリプトを使用する際に、他に注意する必要があることはありますか?


2 遅延機能


2.1 deferスクリプトの実行タイミングについて

遅延機能は HTML4 仕様で定義された拡張機能で、当初は IE4 以降と Firefox3.5 以降でのみサポートされていましたが、後に Chrome などのブラウザでも defer="defer" を使用してサポートが追加されました。 defer は遅延を意味し、スクリプトの実行を遅らせることを意味します。通常であれば、導入したスクリプトはすぐにダウンロードされて実行されますが、遅延機能を使用すると、ダウンロード後すぐにスクリプトが実行されるのではなく、ページが解析された後に実行されます。 HTML4 標準の defer の説明を見てみましょう:

defer: 設定すると、このブール属性は、スクリプトがドキュメント コンテンツを生成しない (JavaScript の "document.write" など) というヒントをユーザー エージェントに提供するため、ユーザー エージェントは解析を続行できます。

言い換えると、defer が設定されている場合は、このスクリプトがドキュメント コンテンツを生成しないことをユーザー エージェントに伝え、ユーザー エージェントが解析とレンダリングを続行できるようにします。 MDN の defer の主要な説明をもう一度見てみましょう:

defer: async 属性が存在しないが defer 属性が存在する場合、ページの解析が終了したときにスクリプトが実行されます。

標準の定義を通じて、遅延スクリプトはページの解析をブロックせず、ページの解析が完了するまで待ってから実行することを明確にできます。ただし、遅延には時間がかかる場合があります。外部リソースのダウンロードをブロックすると、DOMContentLoaded イベントがブロックされますか?実際、遅延スクリプトは DOMContentLoaded イベントの前に引き続き実行されるため、DOMContentLoaded 内のスクリプトは引き続きブロックされます。次の図を使用すると、遅延スクリプトの実行タイミングを理解できます:



標準の定義によれば、内部スクリプトは遅延をサポートしませんが、ブラウザ IE9 以下は内部スクリプトの遅延サポートを提供します。

2.2 ブラウザーのサポートを延期

遅延機能に対するブラウザのサポートを見てみましょう:


IE9 以下のブラウザにはバグがあります。これについては、後のデモで詳しく説明します。

2.3 デモ: 遅延機能の機能検証

オリヴィエ・ロシャールが「スクリプトの遅延属性」で使用したメソッドを模倣して、遅延属性の機能を検証します。

まず、6 つの外部スクリプトを準備しました:

1.js:

test += "私は外部スクリプト n です";

2.js


test += "私はボディ外部スクリプト n";


3.js


test += "私は最下位の外部スクリプト n";

defer1.js

test += "私は外部遅延スクリプト n です";

defer2.js


test += "私は本体外部遅延スクリプト n";


defer3.js


test += "私は最下位の外部遅延スクリプト n";

HTML のコードは次のとおりです:


コードでは、DOMContentLoaded イベントの実装を容易にするために、jQuery を導入しました (後の記事で、互換性のある DOMContentLoaded を自分で実装する方法を紹介します)。次に、ヘッド、ボディ内、および外部に遅延スクリプトを導入しました。スクリプトの本体と通常のスクリプトを作成し、グローバル文字列を通じて各コードの実行ステータスを記録します。各ブラウザーでの実行結果を見てみましょう。

テーブル>

从输出的结果中我们可以确定,只有IE9及以下浏览器支持内部延迟脚本,并且defer后的脚本都会在DOMContentLoaded事件之前触发,因此也是会堵塞DOMContentLoaded事件的。

2.4 DEMO:IE0f630a08ba48c70580451c5807d38ff4我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
document.body.innerHTML += "7a6bc4f2a125532aca32e87dac970576我是后来加入的16b28748ea4df4d9c2150843fecfba68";
alert("我是第1个脚本");

2.js

alert("我是第2个脚本");

修改HMTL中的代码为:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>defer bug in IE=9 test</title>
<script src="1.js" type="text/javascript" defer="defer"></script>
<script src="2.js" type="text/javascript" defer="defer"></script>
</head>
<body>
</body>
</html>

正常情况下,浏览器中弹出框的顺序肯定是:我是第1个脚本-》我是第2个脚本,然而在IE<=9中,执行结果却为:我是第2个脚本-》我是第1个脚本,验证了这个bug。

2.5 defer总结

在总结之前,首先要说一个注意点:正如标准中提到的,defer的脚本中不应该出现document.write的操作,浏览器会直接忽略这些操作。

总的来看,defer的作用一定程度上与将脚本放置在页面底部有一定的相似,但由于IE<=9中的bug,如果页面中出现多个defer时,脚本的执行顺序可能会被打乱从而导致代码依赖可能会出错,因此实际项目中很少会使用defer特性,而将脚本代码放置在页面底部可以替代defer所提供的功能。

3 async特性

3.1 关于async脚本的执行时机

async特性是HTML5中引入的特性,使用方式为:async="async",我们首先看一下标准中对于async特性的相关描述:

async:If the async attribute is present, then the script will be executed asynchronously, as soon as it is available.

需要指出,这里的异步,指的其实是异步加载而不是异步执行,也就是说,浏览器遇到一个async的script标签时,会异步的去加载(个人认为这个过程主要是下载的过程),一旦加载完毕就会执行代码,而执行的过程肯定还是同步的,也就是阻塞的。我们可以通过下图来综合理解defer和async:


这样来看的话,async脚本的执行时机是无法确定的,因为脚本何时加载完毕也是不确定的。我们通过下面的demo来感受一下:

async1.js

alert("我是异步的脚本");

HTML代码:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>async attribute test</title>
<script src="/delayfile.php&#63;url=http://localhost/js/load/async1.js&delay=2" async="async" type="text/javascript"></script>
<script>
alert("我是同步的脚本");
</script>
</head>
<body>
</body>
</html> 

這裡我們借用了《瀏覽器環境下JavaScript腳本載入與執行探析之程式碼執行順序》中的delayfile腳本來提供了一個延遲,這個腳本在支援async的瀏覽器中,彈框的順序一般是:我是同步的腳本-》我是異步的腳本。

3.2 async的瀏覽器支援狀況

下面我們來看看async特性的瀏覽器支援情況:

可以看到,只有IE10+才支援async特性,opera mini不支援async特性,另外,async是不支援內部腳本的。

3.3 async總結

async指的非同步腳本,即腳本非同步加載,加載的過程不會造成阻塞,但是async的腳本的執行時機是不確定的,而且執行的順序也是不確定的,因此使用async的腳本應該是不依賴任何程式碼的腳本(例如第三方統計程式碼或廣告程式碼),否則就會導致執行出錯。

4 defer和async的優先問題

這一點比較好理解,標準中規定了:

[1]如果<script>元素同時定義了defer和async特性,則按async來處理(注意:對於不支援async的瀏覽器會直接忽略async特性)<br /> </script>

[2]如果<script>元素只定義了defer,則以延遲腳本的方式處理<br /> </script>

[3]如果<script>元素沒有定義defer也沒有定義async,則按正常情況處理,即:腳本立即載入和執行<br /> </script>

IE7 IE9 IE10 クローム Firefox
私は外部スクリプトの先頭です
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>defer attribute test</title>
<script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
<script type="text/javascript">var test = "";</script>
<script src="defer1.js" type="text/javascript" defer="defer"></script>
<script src="1.js" type="text/javascript"></script>
<script defer="defer">
test += "我是head延迟内部脚本\n";
</script>
<script>
test += "我是head内部脚本\n";
</script>
</head>
<body>
<button id="test">点击一下</button>
<script src="defer2.js" type="text/javascript" defer="defer"></script>
<script src="2.js" type="text/javascript"></script>
</body>
<script src="defer3.js" type="text/javascript" defer="defer"></script>
<script src="3.js" type="text/javascript"></script>
<script>
$(function(){
test += "我是DOMContentLoaded里面的脚本
";
})
window.onload = function(){
test += "我是window.onload里面的脚本
";
var button = document.getElementById("test");
button.onclick = function(){
alert(test);
}
}
</script>
</html> 
私は内部スクリプトの先頭です
私は本体外部スクリプトです

私は一番下の外部スクリプトです
私は外部遅延スクリプトの先頭です

私はヘッドディレイ内部スクリプトです

私は身体外部遅延スクリプトです

IE7 IE9 IE10 CHROME firefox

我是head外部脚本
我是head内部脚本
我是body外部脚本
我是底部外部脚本
我是head外部延迟脚本
我是head延迟内部脚本
我是body外部延迟脚本
我是底部外部延迟脚本
我是DOMContentLoaded里面的脚本
我是window.onload里面的脚本

我是head外部脚本
我是head内部脚本
我是body外部脚本
我是底部外部脚本
我是head外部延迟脚本
我是head延迟内部脚本
我是body外部延迟脚本
我是底部外部延迟脚本
我是DOMContentLoaded里面的脚本
我是window.onload里面的脚本

我是head外部脚本
我是head延迟内部脚本
我是head内部脚本
我是body外部脚本
我是底部外部脚本
我是head外部延迟脚本
我是body外部延迟脚本
我是底部外部延迟脚本
我是DOMContentLoaded里面的脚本
我是window.onload里面的脚本

我是head外部脚本
我是head延迟内部脚本
我是head内部脚本
我是body外部脚本
我是底部外部脚本
我是head外部延迟脚本
我是body外部延迟脚本
我是底部外部延迟脚本
我是DOMContentLoaded里面的脚本
我是window.onload里面的脚本

<br />我是head外部脚本
<span style="color: rgb(255,0,0)">我是head延迟内部脚本</span>
我是head内部脚本
我是body外部脚本
我是底部外部脚本
我是head外部延迟脚本
我是body外部延迟脚本
我是底部外部延迟脚本
<span style="color: rgb(255,0,0)">我是DOMContentLoaded里面的脚本
我是window.onload里面的脚本</span>
私は一番下の外部遅延スクリプトです 私は DOMContentLoaded のスクリプトです 私は window.onload のスクリプトです
私は外部スクリプトの先頭です 私は内部スクリプトの先頭です 私は本体外部スクリプトです 私は一番下の外部スクリプトです 私は外部遅延スクリプトの先頭です 私はヘッドディレイ内部スクリプトです 私は身体外部遅延スクリプトです 私は一番下の外部遅延スクリプトです 私は DOMContentLoaded のスクリプトです 私は window.onload のスクリプトです 私は外部スクリプトの先頭です 内部スクリプトが遅延しています 私は内部スクリプトの先頭です 私は本体外部スクリプトです 私は一番下の外部スクリプトです 私は外部遅延スクリプトの先頭です 私は身体外部遅延スクリプトです 私は一番下の外部遅延スクリプトです 私は DOMContentLoaded のスクリプトです 私は window.onload のスクリプトです 私は外部スクリプトの先頭です 内部スクリプトが遅延しています 私は内部スクリプトの先頭です 私は本体外部スクリプトです 私は一番下の外部スクリプトです 私は外部遅延スクリプトの先頭です 私は身体外部遅延スクリプトです 私は一番下の外部遅延スクリプトです 私は DOMContentLoaded のスクリプトです 私は window.onload のスクリプトです
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。