Skip to content

Scripts: async, defer #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 23, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 75 additions & 77 deletions 2-ui/5-loading/02-script-async-defer/article.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@

# Scripts: async, defer
# Les scripts: async, defer

In modern websites, scripts are often "heavier" than HTML: their download size is larger, and processing time is also longer.
Dans les sites Web modernes, les scripts sont souvent "plus lourds" que le HTML: leur taille de téléchargement est plus grande et le temps de traitement est également plus long.

When the browser loads HTML and comes across a `<script>...</script>` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts `<script src="..."></script>`: the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page.
Lorsque le navigateur charge le HTML et rencontre une balise `<script>...</script>`, il ne peut pas continuer à construire le DOM. Il doit exécuter le script de suite. Il en va de même pour les scripts externes `<script src ="..."></script>`: le navigateur doit attendre le téléchargement du script, l'exécuter, puis traiter le reste de la page.

That leads to two important issues:
Cela conduit à deux problèmes importants:

1. Les scripts ne peuvent pas voir les éléments DOM en dessous d'eux, ils ne peuvent donc pas ajouter de gestionnaires, etc.
2. S'il y a un script volumineux en haut de la page, il "bloque la page". Les utilisateurs ne peuvent pas voir le contenu de la page tant qu'il n'est pas téléchargé et exécuté:

1. Scripts can't see DOM elements below them, so they can't add handlers etc.
2. If there's a bulky script at the top of the page, it "blocks the page". Users can't see the page content till it downloads and runs:

```html run height=100
<p>...content before script...</p>

<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- This isn't visible until the script loads -->
<!-- Ceci n'est pas visible tant que le script n'est pas chargé -->
<p>...content after script...</p>
```

There are some workarounds to that. For instance, we can put a script at the bottom of the page. Then it can see elements above it, and it doesn't block the page content from showing:
Il existe quelques solutions pour contourner cela. Par exemple, nous pouvons mettre un script en bas de page. Comme ça, il peut voir les éléments au-dessus, et cela ne bloque pas l'affichage du contenu de la page:

```html
<body>
Expand All @@ -29,85 +30,79 @@ There are some workarounds to that. For instance, we can put a script at the bot
</body>
```

But this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay.
Mais cette solution est loin d'être parfaite. Par exemple, le navigateur remarque le script (et peut commencer à le télécharger) uniquement après avoir téléchargé le document HTML complet. Pour les longs documents HTML, cela peut être un retard notable.

Such things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection.
De telles choses sont invisibles pour les personnes utilisant des connexions très rapides, mais de nombreuses personnes dans le monde ont encore des vitesses Internet lentes et utilisent une connexion Internet mobile loin d'être parfaite.

Luckily, there are two `<script>` attributes that solve the problem for us: `defer` and `async`.
Heureusement, il y a deux attributs de `<script>` qui résolvent le problème pour nous: `defer` et `async`.

## defer

The `defer` attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads "in the background", and then runs when the DOM is fully built.
L'attribut `defer` indique au navigateur qu'il doit continuer à travailler avec la page, charger le script "en arrière-plan", puis exécuter le script lors de son chargement.

Here's the same example as above, but with `defer`:
Voici le même exemple que ci-dessus, mais avec `defer`:

```html run height=100
<p>...content before script...</p>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- visible immediately -->
<!-- visible immédiatement -->
<p>...content after script...</p>
```

In other words:

- Scripts with `defer` never block the page.
- Scripts with `defer` always execute when the DOM is ready (but before `DOMContentLoaded` event).
- Les scripts avec `defer` ne bloquent jamais la page.
- Les scripts avec `defer` s'exécutent toujours lorsque le DOM est prêt, mais avant l'événement `DOMContentLoaded`.

The following example demonstrates the second part:
L'exemple suivant montre que:

```html run height=100
<p>...content before scripts...</p>

<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!"));
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); // (2)
</script>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<p>...content after scripts...</p>
```

1. The page content shows up immediately.
2. `DOMContentLoaded` event handler waits for the deferred script. It only triggers when the script is downloaded and executed.
1. Le contenu de la page s'affiche immédiatement.
2. `DOMContentLoaded` attend le script différé. Il ne se déclenche que lorsque le script `(2)` est téléchargé et exécuté.

**Deferred scripts keep their relative order, just like regular scripts.**
Les scripts différés conservent leur ordre relatif, tout comme les scripts classiques.

Let's say, we have two deferred scripts: the `long.js` and then `small.js`:
Donc, si nous avons d'abord un long script, puis un plus petit, alors ce dernier attend.

```html
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
```

Browsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The `small.js` probably finishes first.

...But the `defer` atribute, besides telling the browser "not to block", ensures that the relative order is kept. So even though `small.js` loads first, it still waits and runs after `long.js` executes.
```smart header="Le petit script télécharge en premier, s'exécute en dernier"
Les navigateurs analysent la page à la recherche de scripts et les téléchargent en parallèle pour améliorer les performances. Ainsi, dans l'exemple ci-dessus, les deux scripts se téléchargent en parallèle. Le `small.js` est probablement le premier.

That may be important for cases when we need to load a JavaScript library and then a script that depends on it.

```smart header="The `defer` attribute is only for external scripts"
The `defer` attribute is ignored if the `<script>` tag has no `src`.
Mais la spécification exige que les scripts s'exécutent dans l'ordre des documents, donc elle attend que `long.js` s'exécute.
```

## async
```smart header="L'attribut `defer` est uniquement pour les scripts externes"
L'attribut `defer` est ignoré si la balise `<script>` n'a pas de `src`.
```

The `async` attribute is somewhat like `defer`. It also makes the script non-blocking. But it has important differences in the behavior.

The `async` attribute means that a script is completely independent:
## async

- The browser doesn't block on `async` scripts (like `defer`).
- Other scripts don't wait for `async` scripts, and `async` scripts don't wait for them.
- `DOMContentLoaded` and async scripts don't wait for each other:
- `DOMContentLoaded` may happen both before an async script (if an async script finishes loading after the page is complete)
- ...or after an async script (if an async script is short or was in HTTP-cache)
L'attribut `async` signifie qu'un script est complètement indépendant:

In other words, `async` scripts load in the background and run when ready. The DOM and other scripts don't wait for them, and they don't wait for anything. A fully independent script that runs when loaded. As simple, at it can get, right?
- La page n'attend pas les scripts asynchrones, le contenu est traité et affiché.
- Les scripts `DOMContentLoaded` et async ne s'attendent pas:
- `DOMContentLoaded` peut se produire à la fois avant un script async (si un script async termine le chargement une fois la page terminée)
- ... ou après un script async (si un script async est court ou était en cache HTTP)
- Les autres scripts n'attendent pas les scripts `async`, et les scripts `async` ne les attendent pas.

Here's an example similar to what we've seen with `defer`: two scripts `long.js` and `small.js`, but now with `async` instead of `defer`.

They don't wait for each other. Whatever loads first (probably `small.js`) -- runs first:
Donc, si nous avons plusieurs scripts `async`, ils peuvent s'exécuter dans n'importe quel ordre. Premier chargé -- premier exécuté:

```html run height=100
<p>...content before scripts...</p>
Expand All @@ -122,42 +117,50 @@ They don't wait for each other. Whatever loads first (probably `small.js`) -- ru
<p>...content after scripts...</p>
```

- The page content shows up immediately: `async` doesn't block it.
- `DOMContentLoaded` may happen both before and after `async`, no guarantees here.
- A smaller script `small.js` goes second, but probably loads before `long.js`, so `small.js` runs first. Although, it might be that `long.js` loads first, if cached, then it runs first. In other words, async scripts run in the "load-first" order.
1. Le contenu de la page apparaît immédiatement: `async` ne la bloque pas.
2. `DOMContentLoaded` peut arriver soit avant ou après `async`, aucune garantie ici.
3. Les scripts asynchrones n'attendent pas les uns les autres. Un script plus petit `small.js` passe en second, mais se charge probablement avant `long.js`, donc s'exécute en premier. C'est ce qu'on appelle une commande "load-first".

Async scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don't depend on our scripts, and our scripts shouldn't wait for them:
Les scripts asynchrones sont parfaits lorsque nous intégrons un script tiers indépendant dans la page: compteurs, publicités, etc., car ils ne dépendent pas de nos scripts et nos scripts ne doivent pas les attendre:

```html
<!-- Google Analytics is usually added like this -->
<!-- Google Analytics est généralement ajouté comme ceci -->
<script async src="https://google-analytics.com/analytics.js"></script>
```

## Dynamic scripts

There's one more important way of adding a script to the page.

We can create a script and append it to the document dynamically using JavaScript:
## Les scripts dynamiques

Nous pouvons également ajouter un script dynamiquement en utilisant JavaScript:

```js run
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
```

The script starts loading as soon as it's appended to the document `(*)`.
Le script commence à se charger dès qu'il est ajouté au document `(*)`.

**Les scripts dynamiques se comportent comme "asynchrones" par défaut.**

**Dynamic scripts behave as "async" by default.**
C'est-à-dire:
- Ils n'attendent rien, rien ne les attend.
- Le script qui se charge en premier -- s'exécute en premier ("load-first").

That is:
- They don't wait for anything, nothing waits for them.
- The script that loads first -- runs first ("load-first" order).

This can be changed if we explicitly set `script.async=false`. Then scripts will be executed in the document order, just like `defer`.
```js run
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";

In this example, `loadScript(src)` function adds a script and also sets `async` to `false`.
*!*
script.async = false;
*/!*

document.body.append(script);
```

Par exemple, nous ajoutons ici deux scripts. Sans `script.async=false`, ils s'exécuteraient dans l'ordre de chargement (le `small.js` probablement en premier). Mais avec, l'ordre est "comme dans le document":

So `long.js` always runs first (as it's added first):

```js run
function loadScript(src) {
Expand All @@ -167,35 +170,30 @@ function loadScript(src) {
document.body.append(script);
}

// long.js runs first because of async=false
// long.js s'exécute en premier à cause de async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
```

Without `script.async=false`, scripts would execute in default, load-first order (the `small.js` probably first).

Again, as with the `defer`, the order matters if we'd like to load a library and then another script that depends on it.

## Résumé

## Summary
`Async` et `defer` ont un point commun: le téléchargement de tels scripts ne bloque pas le rendu des pages. Ainsi, l'utilisateur peut lire le contenu de la page et se familiariser immédiatement avec la page.

Both `async` and `defer` have one common thing: downloading of such scripts doesn't block page rendering. So the user can read page content and get acquainted with the page immediately.
Mais il existe également des différences essentielles entre eux:

But there are also essential differences between them:

| | Order | `DOMContentLoaded` |
| | L'ordre | `DOMContentLoaded` |
|---------|---------|---------|
| `async` | *Load-first order*. Their document order doesn't matter -- which loads first runs first | Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. |
| `defer` | *Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. |

In practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important.
| `async` | *Load-first*. Leur ordre dans le document n'a pas d'importance -- premier chargé, premier exécuté | Sans importance. Peut se charger et s'exécuter alors que le document n'a pas encore été entièrement téléchargé. Cela se produit si les scripts sont petits ou mis en cache et que le document est suffisamment long. |
| `defer` | *L'ordre du Document*. | Exécute après le chargement et l'analyse du document (ils attendent si nécessaire), juste avant `DOMContentLoaded`. |

And `async` is used for independent scripts, like counters or ads. And their relative execution order does not matter.
En pratique, `defer` est utilisé pour les scripts qui ont besoin de tout le DOM et/ou leur ordre d'exécution relatif est important.
Et `async` est utilisé pour des scripts indépendants, comme des compteurs ou des publicités. Et leur ordre d'exécution relatif n'a pas d'importance.

```warn header="Page without scripts should be usable"
Please note: if you're using `defer` or `async`, then user will see the the page *before* the script loads.
```warn header="La page sans scripts devrait être utilisable"
Veuillez noter que si vous utilisez `defer`, la page est visible *avant* le chargement du script.

In such case, some graphical components are probably not initialized yet.
L'utilisateur peut donc lire la page, mais certains composants graphiques ne sont probablement pas encore prêts.

Don't forget to put "loading" indication and disable buttons that aren't functional yet. Let the user clearly see what he can do on the page, and what's still getting ready.
Il devrait y avoir des indications de "chargement" aux bons endroits et les boutons désactivés devraient s'afficher comme tels, afin que l'utilisateur puisse voir clairement ce qui est prêt et ce qui ne l'est pas.
```