Maison > interface Web > js tutoriel > Analyse du chargement et de l'exécution de scripts JavaScript dans un environnement de navigateur : caractéristiques différées et asynchrones_compétences Javascript

Analyse du chargement et de l'exécution de scripts JavaScript dans un environnement de navigateur : caractéristiques différées et asynchrones_compétences Javascript

WBOY
Libérer: 2016-05-16 15:20:02
original
1635 Les gens l'ont consulté

Les fonctionnalités de report et async sont considérées comme deux fonctionnalités que de nombreux développeurs JavaScript sont "familières mais pas familières". D'un point de vue littéral, les fonctions des deux sont faciles à comprendre. et "script asynchrone" respectivement. Cependant, en prenant le report comme exemple, les développeurs ne sont pas nécessairement familiers avec certains détails, tels que : quand un script avec la fonctionnalité defer sera-t-il retardé pour être exécuté ; si les scripts internes et les scripts externes peuvent prendre en charge les scripts defer après le report ; en plus de l'exécution différée, quelles sont les particularités, etc. Cet article combine certains articles existants et la description des deux fonctionnalités dans les documents MDN pour mener une étude plus complète et un résumé du report et de l'async, dans l'espoir d'aider les développeurs à mieux maîtriser ces deux fonctionnalités.

1 Introduction

Dans "Analyse du chargement et de l'exécution du script JavaScript dans l'environnement du navigateur : séquence d'exécution du code" nous avons mentionné que l'exécution du code JavaScript bloquera l'analyse et le rendu de la page ainsi que le téléchargement d'autres ressources. . Bien sûr, étant donné que JavaScript est un langage monothread, ce qui signifie que dans des circonstances normales, le code JavaScript d'une page ne peut être exécuté que dans l'ordre de haut en bas. Bien sûr, comme dans " Analyse de JavaScript. Chargement et exécution du script dans l'environnement du navigateur Comme nous l'avons analysé dans « Séquence d'exécution », dans certains cas, par exemple lors de la saisie d'un script via document.write ou de l'introduction d'un script via la technologie de script dynamique, l'ordre d'exécution du code JavaScript ne ne suivent pas nécessairement l'ordre strict de haut en bas. le report et l'async sont aussi ce que nous appelons des « situations anormales ».

Nous disons souvent que l'exécution de JavaScript est bloquante. Dans le développement réel, le blocage qui nous préoccupe généralement le plus et celui qui affecte le plus l'expérience utilisateur devrait être les aspects suivants :

[1] Blocage de l'analyse et du rendu des pages

[2] Le script d'initialisation de la page que nous avons écrit (généralement le script destiné à écouter l'événement DOMContentLoaded). Cette partie du script est le script que nous souhaitons exécuter en premier, car nous écrirons le code le plus pertinent pour l'interaction de l'utilisateur. ici. )

[3] Blocage du téléchargement de ressources externes sur la page (telles que des images)

Si nous avons une opération de script qui prend du temps et que ce script bloque les trois endroits que nous avons mentionnés ci-dessus, alors les performances ou l'expérience utilisateur de cette page Web seront très mauvaises.

L'intention initiale des deux fonctionnalités defer et async est également de résoudre ou d'atténuer l'impact du blocage sur l'expérience de la page. Analysons ces deux fonctionnalités ci-dessous. Nous comprenons ces deux principalement sous les aspects suivants :

[1]Quelle est l'heure d'exécution des scripts retardés ou asynchrones ? Qu'en est-il du blocage de pages ?


[2] Les scripts internes et externes sont-ils capables d'une mise en œuvre retardée ou asynchrone ?


[3] Dans quelle mesure les navigateurs prennent-ils en charge ces deux fonctionnalités ? Y a-t-il des bugs associés ?


[4] Y a-t-il autre chose auquel il faut prêter attention lors de l'utilisation de scripts qui utilisent ces deux fonctionnalités ?


Fonction de 2 reports

2.1 À propos du timing d'exécution du script de report

La fonctionnalité de report est une fonctionnalité étendue définie dans la spécification HTML4. Initialement, elle n'était prise en charge que par IE4 et Firefox 3.5. Plus tard, des navigateurs tels que Chrome l'ont également ajouté, en utilisant defer="defer". defer signifie retard, ce qui signifie que cela retardera l'exécution du script. Dans des circonstances normales, le script que nous introduisons sera téléchargé et exécuté immédiatement. Cependant, avec la fonction de report, le script ne sera pas exécuté immédiatement après le téléchargement, mais sera exécuté après l'analyse de la page. Jetons un coup d'œil à l'explication du report dans la norme HTML4 :


defer : lorsqu'il est défini, cet attribut booléen indique à l'agent utilisateur que le script ne générera aucun contenu de document (par exemple, pas de "document.write" en javascript) et que l'agent utilisateur peut donc continuer l'analyse. et le rendu.


En d'autres termes, si defer est défini, il indique à l'agent utilisateur que ce script ne produira aucun contenu de document, afin que l'agent utilisateur puisse continuer à analyser et à restituer. Jetons un autre coup d'œil à la description clé du report dans MDN :


defer : Si l'attribut async n'est pas présent mais que l'attribut defer est présent, alors le script est exécuté lorsque la page a terminé l'analyse

.

Grâce à la définition de la norme, nous pouvons indiquer clairement que le script defer ne bloquera pas l'analyse de la page, mais attendra que l'analyse de la page soit terminée avant de l'exécuter. Cependant, le defer, qui prend beaucoup de temps, peut toujours. bloquer le téléchargement de ressources externes, puis bloquera-t-il l'événement DOMContentLoaded ? En fait, le script defer est toujours exécuté avant l'événement DOMContentLoaded, il bloquera donc toujours le script dans DOMContentLoaded. Nous pouvons utiliser la figure suivante pour aider à comprendre le timing d'exécution du script defer :



Selon la définition de la norme, les scripts internes ne prennent pas en charge le report, mais les navigateurs IE9 et inférieurs fournissent une prise en charge différée pour les scripts internes.


2.2 différer la prise en charge du navigateur

Jetons un coup d'œil à la prise en charge par les navigateurs de la fonctionnalité de report :


Il y a un bug dans les navigateurs IE9 et inférieurs, qui sera expliqué en détail plus tard dans la DÉMO.

2.3 DÉMO : Vérification du fonctionnement de la fonction de report

Nous imitons la méthode utilisée par Olivier Rochard dans "le script defer attribut" pour vérifier la fonction de l'attribut defer :

Nous avons d'abord préparé 6 scripts externes :

1.js:

test = "Je suis responsable du script externe n"

2.js

test = "Je suis un corps externe script n"

3.js

test = "Je suis le script externe du bas n"

defer1.js

test = "Je suis responsable du script de delay externe n"
;

defer2.js

test = "Je suis un script de retard externe du corps n"
;

defer3.js

test = "Je suis le script de retard externe inférieur n"
;

Le code en HTML est :

<!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> 
Copier après la connexion

Dans le code, afin de faciliter l'implémentation de l'événement DOMContentLoaded, nous avons introduit jQuery (des articles ultérieurs expliqueront comment implémenter vous-même un DOMContentLoaded compatible. Ensuite, nous avons introduit des scripts de retard dans la tête, à l'intérieur du corps et à l'extérieur du). corps du script et des scripts normaux, et enregistre l'état d'exécution de chaque morceau de code via une chaîne globale. Jetons un coup d'œil aux résultats d'exécution dans chaque navigateur :
.

IE7 IE9 IE10 CHROME firefox
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>
Copier après la connexion
Je suis responsable script externe<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis responsable du script interne du retard<🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜>
<🎜>Je suis responsable script externe<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis responsable du script interne du retard<🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> <🎜>Je suis responsable script externe<🎜> Je suis en tête du script interne retardé<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> <🎜>Je suis responsable script externe<🎜> Je suis en tête du script interne retardé<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> <🎜>

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

2.4 DEMO:IE<=9的defer特性bug

从2.3节中的demo可以看出,defer后的脚本还是能够保持执行顺序的,也就是按照添加的顺序依次执行。而在IE<=9中,这个问题存在一个bug:假如我们向文档中增加了多个defer的脚本,而且之前的脚本中有appendChild,innerHTML,insertBefore,replaceChild等修改了DOM的接口调用,那么后面的脚本可能会先于该脚本执行。可以参考github的issue:https://github.com/h5bp/lazyweb-requests/issues/42

我们通过DEMO验证一下,首先修改1.js的代码为(这段代码只为模拟,事实上这段代码存在极大的性能问题):

document.body.innerHTML = "

我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
document.body.innerHTML += "
我是后来加入的
";
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>
Copier après la connexion

正常情况下,浏览器中弹出框的顺序肯定是:我是第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> 
Copier après la connexion

여기서는 "브라우저 환경의 JavaScript 스크립트 로딩 및 실행 분석: 코드 실행 순서"에서 지연 파일 스크립트를 빌려 비동기를 지원하는 브라우저에서 이 스크립트의 팝업 상자 순서가 일반적입니다. : 나는 동기 스크립트입니다 -> 나는 비동기 스크립트입니다.

3.2 브라우저 비동기 지원

비동기 기능에 대한 브라우저 지원을 살펴보겠습니다.

보시다시피 IE10만 비동기 기능을 지원하고, Opera Mini는 비동기 기능을 지원하지 않으며, 비동기는 내부 스크립트를 지원하지 않습니다.

3.3 비동기 요약

async는 비동기식 스크립트를 의미합니다. 즉, 스크립트가 비동기적으로 로드되는 과정은 블로킹을 일으키지 않지만, 비동기 스크립트의 실행 시기도 불확실하고, 실행 순서도 불확실하므로 비동기를 사용하는 스크립트는 반드시 코드(예: 타사 통계 코드 또는 광고 코드)에 의존하지 않는 스크립트. 그렇지 않으면 실행 오류가 발생합니다.

지연과 비동기 사이의 4가지 우선순위 문제

이 표준은 다음과 같이 규정하고 있습니다.

[1]

Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal