Fermer

décembre 7, 2021

Introduction aux processus, aux threads – Interface utilisateur Web


Au lieu de tout faire en un seul processus sur un seul thread, les navigateurs modernes ont une architecture multi-processus. Qu'est-ce que cela signifie pour le développement en JavaScript, qui est monothread ?

Nous entendons souvent parler de « thread principal » lorsque nous apprenons à optimiser le code JavaScript pour les performances.

JavaScript est monothread. Une seule chose peut arriver à la fois, sur un seul thread principal et tout le reste est bloqué jusqu'à la fin d'une opération. — MDN

Il m'a fallu un certain temps pour réaliser que le thread principal dont nous parlons appartient à un processus dans le navigateur qui est spécifiquement responsable du rendu de la page Web et de l'exécution de tout ce qui affecte le rendu (JavaScript et événements d'entrée utilisateur). Les navigateurs modernes ont une architecture multiprocessus avec des processus séparés qui prennent en charge différentes parties du navigateur.

La connaissance des processus et des threads m'a également aidé à voir que les API Web introduisent des tâches asynchrones dans nos applications. Lorsque nous appelons des API Web telles que fetch(url).then(cb) ou setTimeout(cb, delay)elles ne s'exécutent pas sur le thread principal du processus de rendu. Par exemple, fetch() s'exécute sur un thread réseau dans le processus du navigateur.

À l'aide de l'API Web Workers, nous pouvons exécuter des tâches gourmandes en ressources CPU sur un thread d'arrière-plan du processus de rendu. De plus, nous pouvons utiliser l'API requestIdleCallback() pour mettre en file d'attente des tâches chronophages et de faible priorité à exécuter sur le thread principal du processus de rendu lorsque le navigateur serait autrement inactif.

Lors de la programmation avec JavaScript, nous n'avons généralement pas à penser aux threads. Cependant, une compréhension de base des threads et des processus permet d'éclaircir certains des mystères de la programmation asynchrone en JavaScript. Par conséquent, dans cet article, nous parlerons des processus, des threads, des responsabilités du thread principal du processus de rendu et de son interaction avec les autres processus et threads du navigateur.

Sur le côté gauche d'un chas d'aiguille. est du fil blanc. A droite, un arc-en-ciel de fils.

Crédit photo : John Anvik sur Unsplash.

Avant de parler de processus et les threads, nous devons examiner la différence entre les langages compilés et interprétés. code machine que les ordinateurs peuvent exécuter. Les langages de programmation peuvent être classés en langages compilés ou interprétés.

Quelle est la différence entre les deux ?

Langages compilés

Les applications écrites avec des langages compilés sont compilées pour produire du code machine qui est exécuté directement par le système d'exploitation. L'application est compilée à l'aide d'un compilateur. La compilation d'une application est souvent appelée l'étape de « construction ». L'étape de construction produit un fichier exécutable contenant le code machine.

Le fichier exécutable est empaqueté et mis à la disposition des utilisateurs afin qu'ils puissent l'installer sur leurs appareils.

Par exemple, Google Chrome est une application écrite avec un fichier compilé. langage (principalement C++).

Icône de l'application Google Chrome

Lorsque nous exécutons l'application Chrome, par exemple en cliquant sur l'icône, le système d'exploitation de notre appareil crée un processus pour exécuter l'application .

Langues interprétées

Une langue interprétée utilise un interpréteur pour analyser le code de l'application, traduire en instructions que l'interprète peut comprendre, puis exécuter les instructions. Les interprètes eux-mêmes sont des programmes (écrits en langage assembleur ou langage de haut niveau).

JavaScript est un langage interprété utilisé pour construire des applications Web. Les navigateurs tels que Google Chrome disposent d'un moteur JavaScript doté d'un interpréteur pour traduire le code JavaScript et l'exécuter.

Nous savons maintenant que les applications compilées sont compilées pour produire du code machine exécuté directement sur l'ordinateur de l'utilisateur, alors que les applications interprétées sont analysé, traduit et exécuté par un interprète. Voyons ensuite comment les processus et les threads s'intègrent dans l'image.

Processus and Threads

Process

Lorsque nous exécutons une application qui a été écrite avec un langage compilé (par exemple, en double-cliquant sur son fichier exécutable ), le système d'exploitation démarre un processus.

Démarrer un processus signifie que le système d'exploitation effectue les opérations suivantes :

  • Charge le code binaire de l'application en mémoire
  • Alloue un bloc de mémoire pour que l'application conserve son état (un tas)
  • Démarre un thread d'exécution

Ainsi, un processus est une instance de l'application en exécution. Il comprend le bytecode de l'application en mémoire, un tas et un thread. Le tas stocke l'état de l'application, tandis que le thread est le flux réel d'exécution à travers le code binaire.

Une application peut créer des threads supplémentaires pour exécuter des parties des instructions.

Un processus peut également demander au système d'exploitation de créer processus enfants pour contrôler des parties distinctes de l'application. Le système d'exploitation alloue un espace mémoire distinct à chaque processus. Les processus ne partagent pas les ressources. Au lieu de cela, ils communiquent entre eux à l'aide d'un mécanisme appelé Inter-Process Communication (IPC).

Thread

Comme nous l'avons mentionné précédemment, un processus peut créer des threads supplémentaires. Nous appelons le thread principal d'exécution thread principalet les threads créés pour exécuter des parties du programme threads d'arrière-plan.

Les threads représentent des contextes d'exécution indépendants au sein d'un traiter. Dans un processus multithread, chaque thread a ses propres pilepointeur de pilecompteur de programme et registres spécifiques au thread à conserver suivi de son exécution.

Maintenant que nous avons un aperçu général des processus et des threads, parlons de l'architecture multiprocessus utilisée par les navigateurs dans le but de voir où les applications Web s'intègrent.

Les navigateurs modernes ont un Architecture multi-processus

Les navigateurs sont construits à l'aide de langages compilés. Au lieu de tout faire dans un seul processus sur un seul thread, les navigateurs modernes ont une architecture multi-processus.

Les navigateurs créent plusieurs processus, chacun étant responsable d'une partie différente des fonctionnalités du navigateur. Les processus créent à leur tour plusieurs threads pour exécuter des programmes simultanément.

Une architecture multi-processus offre aux navigateurs une meilleure sécurité :

  • Chaque processus a sa propre mémoire et ses propres ressources accessibles uniquement par les threads au sein du processus
  • Stabilité : si un processus s'exécute lentement ou ne répond plus 🐌🤕, il peut être redémarré sans affecter les autres processus 🏃🏽‍♀️🎊

Regardons Google Chrome pour un exemple. Lorsque nous ouvrons un navigateur Chrome, nous exécutons l'application Chrome. Le système d'exploitation crée un processus — il s'agit du processus principal de Chrome que Chrome appelle à juste titre le processus du navigateur.

Le processus du navigateur crée d'autres processus enfants pour contrôler diverses parties du navigateur. Voici quelques-uns des processus dans Chrome :

  • Processus de navigateur
  • Processus de rendu
  • Processus GPU
  • Processus de plug-in
  • Processus d'extensions
  • Processus d'utilitaires

Les noms de processus reflètent leurs fonctions . Veuillez vous référer à "Inside look at modern web browser" par Mariko Kosaka pour une explication magnifiquement illustrée et détaillée des processus dans Chrome.

En tant que développeurs Web, nous sommes particulièrement intéressés. dans le processus de rendu et son interaction avec le processus de navigateur principal.

Le processus de navigateur contrôle la partie « navigateur » de l'application, y compris la barre d'adresse, les signets, les boutons Précédent et Suivant. Il gère également les parties invisibles et privilégiées d'un navigateur Web, telles que les demandes réseau et l'accès aux fichiers.

Tandis que le processus de rendu contrôle le rendu réel de la page Web. — Mariko Kosaka

Super ! Nous savons maintenant que le processus de rendu est responsable du rendu des pages Web. Examinons de plus près ce que signifie réellement le rendu et comment le processus de rendu le fait. maintient les dommages contenus. Le navigateur lui-même est sécurisé et les autres onglets sont sécurisés. 🔐

Afin de parler du rôle du processus de rendu, parlons d'abord de ce qu'est le rendu.

Qu'est-ce que le rendu ?

Le rendu est le processus de transformation du contenu HTML en pixels. — Steve Kobes

Un document HTML contient le code d'une application Web (éléments HTML, contenu textuel, contenu intégré tel que des images, CSS et JavaScript). Le processus de rendu transforme le document HTML en une page Web que les utilisateurs peuvent voir sur leur écran et avec laquelle ils peuvent interagir. Le document HTML dans une application Angular peut ressembler à ceci :

// index.html

  
<html>  
  <head>  
    `<link rel="stylesheet" href="styles.css" media="print " onload="this.media='all'">[19659084]`
    <style>
        
    </style>  
    `<link href="https://fonts.googleapis .com/icon?family=Material+Icons" rel="stylesheet">`
  </head>  
  <body>  
    <app-root></ app-root>  
    <script src="runtime.js" defer >  
    <script src="polyfills.js" defer>[19659068]<script src="vendor.js" defer>  
    <script src="main.js" defer>  
  </body>[19659084]
</html>

Lorsque nous visitons un site Web, le processus du navigateur récupère le document HTML du site à partir du cache ou du service worker, ou envoie une requête réseau au serveur hébergeant le site Web.

Le processus du navigateur envoie ensuite le document HTML au moteur de rendu. processus de rendu de la page Web.

Le rendu d'une page implique :

  • Effectuer le chemin de rendu critique
  • Charger JavaScript, interpréter JavaScript en code binaire et exécuter le code binaire
  • Peindre les pixels réels sur l'écran

Le processus de rendu utilise un moteur de rendu pour exécuter les étapes du chemin de rendu. Examinons ensuite de plus près le moteur de rendu et le chemin de rendu critique.

Moteur de rendu

Les navigateurs utilisent des moteurs de rendu pour afficher les pages Web.

Un moteur de rendu est un logiciel qui :

  • Implémente le spécifications de la plate-forme Web
  • Effectue le chemin de rendu critique
  • Intégre le moteur JavaScript

Les exemples de moteurs de rendu incluent Blink (Chrome), Gecko (Mozilla) et WebKit (Apple).

Critical Rendering Path

Le moteur de rendu passe par une séquence d'étapes appelée chemin de rendu critique pour transformer un document HTML (HTML, CSS et JavaScript) en pixels dessinés sur l'écran de l'utilisateur.

Le moteur de rendu effectue les étapes suivantes pendant le chemin de rendu critique :

  • Analyse le code HTML et commence à créer le modèle objet de document (DOM)
  • Demande des ressources externes (feuilles de style, scripts, images, etc.)
  • Analyse les styles et crée le modèle d'objet CSS (CSSOM )[19659029] Calcule les styles pour les nœuds visibles dans l'arbre DOM et crée un arbre de rendu qui contient les styles calculés
  • Détermine la géométrie visuelle (largeur, hauteur et position) des éléments en fonction de la taille de la fenêtre (et de l'orientation pour les appareils mobiles )
  • Peint les pixels sur l'écran

Nous pouvons classer le rendu en deux parties :

  1. Rendu de la page d'accueil de l'application lors du premier chargement de l'application
  2. Mettre à jour le rendu au fur et à mesure de l'exécution de l'application, en réponse à l'interaction de l'utilisateur , navigation, défilement, etc.

Le rendu initial commence à partir de zéro. De l'analyse du document HTML à la création de toutes les structures de données (DOM, CSSOM, arbre de rendu, arbre de mise en page, etc.), à la peinture de la page entière, au téléchargement, au traitement et à l'exécution de JavaScript, puis à l'enregistrement des écouteurs d'événements pour rendre la page interactive .

Pendant l'exécution de l'application, le code JavaScript peut mettre à jour le contenu, la structure et les styles du document à l'aide de l'API DOM. Le moteur de rendu met à jour le rendu pour refléter les modifications apportées par JavaScript.

Je recommande vraiment de regarder Life of a Pixel de Steve Kobes (20192018) pour un approfondissement regardez le pipeline de rendu dans Blink (le moteur de rendu de Chrome). Cette conférence est vraiment incroyable, et vous serez ravi de la quantité d'apprentissage que vous en retirerez.

JavaScript Engine

Comme JavaScript est un langage interprété, nous avons besoin d'un interpréteur pour convertir Code JavaScript en code machine, puis exécutez-le.

Les navigateurs ont un moteur JavaScript qui comprend un analyseurun interpréteur et un optimiseur. La plupart des principaux navigateurs ont leur propre implémentation du moteur JavaScript. Le moteur JavaScript de Chromium s'appelle V8.

Comme nous l'avons mentionné précédemment, le moteur de rendu du navigateur intègre son moteur JavaScript. Par exemple, le moteur de rendu de Chrome (Blink) crée une instance de V8 (le moteur JavaScript) — une instance de V8 est appelée un Isolate. 🦋

Tout ce qui interagit avec le DOM doit s'exécuter sur le thread principal pour éviter les problèmes de synchronisation. Étant donné que JavaScript peut modifier le contenu, la structure et les styles des éléments de la page Web à l'aide de l'API DOM, il est logique que JavaScript s'exécute sur le fil principal du processus de rendu.

Comme nous l'avons vu précédemment, les scripts de l'application sont chargés pendant le chemin critique de rendu. Une fois les scripts chargés, le moteur JavaScript utilise ses divers composants pour analyser, interpréter, exécuter et optimiser le JavaScript.

En utilisant Chrome comme exemple, le moteur JavaScript effectue les tâches suivantes :

  • L'analyseur analyse le JavaScript pour créer un AST.
  • L'interpréteur (Ignition) dispose d'un générateur de bytecode qui parcourt l'AST et génère un flux de bytecode.
  • L'interpréteur exécute le bytecode, un bytecode à la fois.
  • Le compilateur d'optimisation (TurboFan ) génère un code optimisé.

Veuillez vous référer à Life of a Script pour en savoir plus sur la façon dont JavaScript est chargé, analysé, compilé et optimisé dans Chrome.

Maintenant, nous voyons cela lorsque nous disons que JavaScript est monothread car il s'exécute sur un seul thread principal, nous parlons du thread principal du processus de rendu. Nous savons que le moteur de rendu du navigateur s'exécute sur le thread principal du processus de rendu, le moteur de rendu crée une instance du moteur JavaScript et le moteur JavaScript crée une pile d'appels JavaScript pour suivre l'exécution du JavaScript de l'application.

Je tiens à souligner ici que la pile d'appels JavaScript n'est pas la même que la pile créée par le système d'exploitation pour le thread principal. Je le pensais naïvement au début et j'étais assez confus. 😆

Thread principal du processus de rendu

Je suis sûr que nous sommes maintenant tout à fait conscients de l'importance du thread principal du processus de rendu. Nous savons que le moteur de rendu et le moteur JavaScript s'exécutent tous les deux sur le thread principal du processus de rendu. Ainsi, le thread principal effectue la majeure partie du travail dans le processus de rendu. pendant le chemin de rendu critique

  • Expose l'API DOM au JavaScript de l'application
  • Mise à jour le rendu
  • Répond aux entrées de l'utilisateur (accepte les événements des périphériques d'entrée et distribue ces événements aux éléments qui devraient les recevoir)
  • Interprétation et exécute le JavaScript de l'application (à l'exception des travailleurs)
  • Le thread principal a une boucle d'événement qui orchestre l'exécution de JavaScript, la mise à jour du rendu et la réponse aux entrées de l'utilisateur. Un thread ne peut exécuter qu'une seule tâche à la fois. Par conséquent, pendant que le thread principal exécute JavaScript, il ne peut pas mettre à jour le rendu ni répondre aux entrées de l'utilisateur. Il est important que le JavaScript de notre application ne bloque pas le thread principal – une fonction qui prend trop de temps à s'exécuter bloque le thread principal jusqu'à ce qu'il finisse de s'exécuter.

    Comme nous le voyons, le processus de rendu ne peint pas réellement les pixels réels à l'écran. . Alors qui le fait ?

    Peindre les pixels à l'écran

    Parler de peindre des pixels me fait penser à cette chanson d'Alice au pays des merveilles de Disney 😊 :

    Nous peignons les roses en rouge. 🌹
    Nous n'osons pas nous arrêter,
    Ou gaspiller une goutte,
    Alors laissez la peinture se répandre.

    Comme l'explique Steve Kobes dans son discours , Life of a Pixel, les navigateurs utilisent la bibliothèque graphique fournie par le système d'exploitation sous-jacent pour peindre les pixels réels sur l'écran de l'utilisateur. La plupart des plates-formes utilisent une API standardisée appelée OpenGL. Il existe également des API plus récentes telles que Vulkan.

    Cependant, les processus de rendu sont mis en bac à sable pour la sécurité afin de protéger l'appareil de l'utilisateur des applications Web et de protéger les autres processus des exploitations de toute vulnérabilité de sécurité dans le processus de rendu. Par conséquent, les programmes exécutés sur le processus de rendu ne peuvent pas effectuer d'appels système pour demander des services au système d'exploitation.

    Le processus de rendu communique avec le processus GPU pour peindre les pixels réels sur l'appareil de l'utilisateur à l'aide de la bibliothèque graphique. Le navigateur fait confiance au code exécuté sur le processus GPU car il s'agit de son propre code, le processus GPU peut donc effectuer des appels système.

    API Web

    Les API Web permettent aux applications Web d'accéder aux fichiers, microphone, caméra, géolocalisation de l'utilisateur , etc. avec l'autorisation de l'utilisateur.

    Les API Web sont intégrées aux navigateurs Web. Les API Web exposent les données du navigateur et de l'environnement informatique environnant. — MDN

    Exemples d'API Web :

    • API DOM
    • setTimeOut()
    • API de récupération
    • Client- API de stockage latéral
    • API de périphérique
    • API de média

    Alors que les méthodes de l'API DOM s'exécutent de manière synchrone, les autres méthodes de l'API Web s'exécutent de manière asynchrone.

    Par exemple, si nous appelons document.createElement() le moteur JavaScript ajoute séquentiellement le contexte d'exécution de la méthode sur la pile d'appels JavaScript même si la pile d'appels n'est pas vide.

    Alors que, si nous appelons le setTimeout() qui est une API Web, le processus de rendu demande à un autre processus (peut-être le processus du navigateur) de démarrer le minuteur, et lorsque le temps spécifié est écoulé, le processus du navigateur met en file d'attente le rappel que nous avons envoyé setTimeout() afin qu'il puisse s'exécuter sur le thread principal du processus de rendu.

    Le navigateur utilise des files d'attente de rappel (également appelées files d'attente de travaux, files d'attente de tâches ou messa ge files) et une file d'attente de microtâches, pour mettre en file d'attente les rappels prêts à être exécutés sur le thread principal. Une boucle d'événements exécute les rappels en attente dans la file d'attente lorsque la pile d'appels JavaScript devient vide.

    Worker Threads

    Enfin, nous sommes arrivés aux threads de travail. Que sont les threads de travail ?

    Les navigateurs nous fournissent l'API Web Workers afin que nous puissions décharger les opérations gourmandes en ressources CPU dans nos applications Web du thread principal vers les threads d'arrière-plan du processus de rendu. Ces threads d'arrière-plan sont également appelés worker threads ou workers.

    Nous utilisons l'interface Workerdisponible sur l'objet global window , pour créer un Web Worker. (Le navigateur expose une variable globale window représentant la fenêtre dans laquelle le script s'exécute en code JavaScript. L'objet window inclut des éléments disponibles dans le monde entier.)

    Le moteur JavaScript crée un nouveau thread de travail et charge le script nommé pour qu'il s'exécute en parallèle avec le thread principal. L'API DOM, CSSOM et d'autres structures de données créées pendant le chemin de rendu critique existent sur le thread principal. Par conséquent, les scripts exécutés dans les threads de travail ne peuvent pas accéder à l'API DOM.

    
    
    if (window.Worker) {  
      const myWorker = nouveau Travailleur('worker.js');
    
      myWorker.onmessage = function(e) {
        console.log(e.data);  
      }  
    }
    

    Le thread principal et le thread de travail communiquer en s'envoyant des messages à l'aide de la méthode postMessage(). Et ils répondent aux messages via le gestionnaire d'événements onmessage. L'événement de message a un attribut de données qui contient le message. 🐠🐠

    
    
    const result = doCpuIntensiveWork();  
    postMessage(result); 
    
    function doCpuIntensiveWork() {}
    

    Les scripts exécutés dans le thread de travail sont déjà dans l'espace de travail afin qu'ils puissent accéder à postMessage() directement.

    Veuillez vous référer à MDN pour en savoir plus sur les Web Workers et les Angular docs pour savoir comment créer des travailleurs dans Angular.

    Résumé

    In cet article, nous avons vu que les navigateurs sont construits à l'aide de langages compilés. Au lieu de tout faire en un seul processus sur un seul thread, les navigateurs modernes ont une architecture multi-processus. L'architecture multiprocessus permet aux navigateurs de fournir aux applications Web la sécurité et la stabilité nécessaires.

    Nous avons appris que les navigateurs utilisent un moteur de rendu pour afficher les pages. Le moteur de rendu implémente les spécifications de la plate-forme Web, effectue le chemin de rendu critique et intègre un moteur JavaScript. JavaScript est un langage interprété. Par conséquent, le moteur JavaScript comprend un interpréteur qui traduit le code JavaScript en code binaire. Le moteur JavaScript crée une pile d'appels JavaScript pour suivre l'exécution du code JavaScript.

    Le thread principal du processus de rendu est responsable du rendu des pages Web et exécute tout ce qui affecte le rendu pour éviter les problèmes de synchronisation. Les événements JavaScript et d'entrée utilisateur peuvent affecter le rendu en manipulant le DOM ou les styles. Par conséquent, en plus d'effectuer le chemin de rendu critique, le thread principal exécute JavaScript (à l'exception des travailleurs) et accepte les événements des périphériques d'entrée et distribue ces événements aux éléments qui devraient les recevoir. La boucle d'événements orchestre l'exécution de ces tâches sur le thread principal.

    Les API Web introduisent des tâches asynchrones dans notre application. Les tâches asynchrones s'exécutent sur d'autres threads en fonction de l'API Web appelée (thread d'arrière-plan du processus de rendu ou thread d'un autre processus). Nous transmettons les rappels à l'appel de l'API Web ou à une promesse renvoyée par l'appel. Lorsque la tâche asynchrone a fini de s'exécuter, elle ajoute le rappel avec le résultat à une file d'attente dans le thread principal. La boucle d'événements exécute les rappels en file d'attente sur le thread principal du processus de rendu lorsque la pile d'appels JavaScript est vide, garantissant ainsi que le code synchrone s'exécute avant le code asynchrone. regardez le processus de rendu et son interaction avec les autres processus du navigateur vous aide à comprendre la nature synchrone et asynchrone du code dans nos applications frontend.

    Ressources

    1. Life of a Pixel (2018 , 20192020)
    2. Vie d'un processus
    3. Philip Roberts : Aide, je suis coincé dans une boucle d'événement .
    4. SmashingConf London—Jake Archibald sur "The Event Loop"
    5. Chromium's Multi-process Architecture
    6. Chrome University[19659029]Comment fonctionne Blink






    Source link