Recherche de site Web

Rationalisez votre code JavaScript avec une attente de niveau supérieur


Empêchez votre code JavaScript de se bloquer ou de renvoyer des erreurs en attendant des ressources externes ou des processus de longue durée.

JavaScript est un langage de programmation populaire qui a commencé comme un langage synchrone à thread unique, ce qui signifie qu'une étape s'exécute après une autre étape sans attendre de récupérer des ressources externes ou d'effectuer un calcul ou un processus long. Si le script nécessite une telle ressource ou un tel calcul, ce comportement synchrone entraîne une erreur. Cela empêche tous les autres processus de la file d'attente de s'exécuter, qu'ils dépendent ou non de l'étape qui a produit l'erreur.

Mais il y a quelque temps, JavaScript a introduit une nouvelle fonctionnalité qui permettait d'attendre uniquement le code nécessitant le chargement d'une ressource externe ou un long processus pour se terminer lors du traitement et du rendu du reste du code. Ce comportement asynchrone est obtenu en utilisant des rappels ou des promesses, qui fonctionnent au niveau de la fonction.

Que sont les rappels et les promesses ?

Je vais expliquer ces concepts à l'aide du code. Si vous savez déjà ce que sont les rappels et les promesses, n'hésitez pas à passer directement à la section d'attente de niveau supérieur et à l'exemple d'application.

Rappels

Dans un rappel, une fonction est transmise à une autre fonction en tant qu'argument ; par conséquent, le deuxième argument de la fonction addEventListener ci-dessous est une fonction de rappel. Ce rappel attendra que le premier argument se produise et, alors seulement, exécutera le corps du deuxième argument.

const x = document.getElementsByTagName('Button');
x[0].addEventListener('click',() =>{alert("I was clicked")})

Ce comportement d'attente rend le code asynchrone. Ceci est différent du code synchrone, dans lequel les tâches sont exécutées les unes après les autres sans attendre le téléchargement d’une ressource ou un long calcul à traiter. Notez cependant que toutes les fonctions de rappel ne sont pas asynchrones.

Promesses

Les promesses sont similaires aux rappels dans le sens où elles attachent une fonction à l'objet renvoyé. Il existe cependant des différences entre les rappels et les promesses. Les promesses sont conçues principalement pour les fonctions asynchrones. Ils n'ont qu'un seul argument, et une fonction .then() est chaînée après les résultats une fois son seul argument renvoyé. En outre, plusieurs fonctions .then() et catch() peuvent y être attachées.

fetch('www.xyz.com/api')
.then((res)=>{let x = res.data; //do something with received data})
.catch((err)=>{console.log(err)})

Les promesses utilisent une file d'attente d'événements et suivent strictement l'ordre dans lequel les tâches sont enchaînées.

Asynchrone/attendre

Async/await est la modification syntaxique des promesses pour éviter le chaînage. Cela rend le code beaucoup plus propre et plus facile à comprendre. Le mot-clé await arrête le code jusqu'à ce que la promesse soit résolue ou rejetée.

async function asyncwaitcode(){
  let getData = await axios('www.xyzdata.org/api')
  console.log(getData.data)
} 

Qu'est-ce que l'attente de haut niveau ?

Tous les exemples ci-dessus rendent les étapes des blocs fonctionnels asynchrones et aucun ne fonctionne au niveau modulaire.

Cependant, un comportement asynchrone peut être obtenu au niveau modulaire. Le but de l'utilisation de l'attente de niveau supérieur est de permettre aux modules d'initialiser de manière asynchrone leur espace de noms avant d'informer le consommateur que le module a terminé son évaluation.

L'exemple d'application suivant utilise une attente de niveau supérieur pour montrer comment cela fonctionne.

À propos de l'application

Cette application récupérera les principales données d'actualités à partir d'une API d'actualités et les restituera localement dans le navigateur. Cette application dispose également d'une fonctionnalité de recherche permettant aux utilisateurs d'obtenir des données d'actualité sur un terme recherché. Avant de commencer, il y a quelques mises en garde à connaître :

  • L'attente de niveau supérieur est prise en charge dans la version de nœud 13.3 et supérieure, et aucune de ces versions ne dispose de LTS (support à long terme) pour le moment.
  • L'attente de niveau supérieur est prise en charge uniquement dans le module ECMAScript, et Node.js et Express utilisent les modules CommonJS. Cependant, la prise en charge du module ECMAScript a été ajoutée dans Node.js. Ainsi, au lieu d'utiliser require pour récupérer les modules, j'utilise import dans toute l'application. CommonJS ne prend pas en charge l'attente de niveau supérieur.
  • L'attente de niveau supérieur ne fonctionne pas avec l'exportation nommée ; cela ne fonctionne qu'avec les exportations par défaut.
  • Dans les versions antérieures à Node.js 14.x, l'attente de niveau supérieur ne fonctionne pas immédiatement. Au lieu de cela, vous devez utiliser l'indicateur d'attente de niveau supérieur --harmony du moteur Google V8 pour l'exécuter. Cependant, il est entièrement pris en charge dans 14.x et versions ultérieures (c'est-à-dire qu'il s'exécute sans indicateurs).
  • L'attente de niveau supérieur ne fonctionne pas avec les scripts classiques et les fonctions non asynchrones.
  • Les dépendances de modules circulaires pourraient introduire une impasse.

Conditions préalables

  • Node.js version 13.3 ou supérieure
  • npm
  • Clés API pour les API d'actualités (voir ci-dessous)

Créer l'application

  1. Tout d'abord, créez un nouveau répertoire nommé toplevelawait :

    $ mkdir toplevelawait
  2. Exécutez npm init pour créer un fichier package.json :

    $ npm init
  3. Ajoutez "type": "module" à package.json pour ajouter la prise en charge des modules ECMAScript :

    "author": "","license": "ISC","type": "module",
  4. Créez un dossier src dans le dossier toplevelawait. Dans le dossier src, créez un répertoire views pour enregistrer les fichiers JavaScript (.ejs) intégrés.

    $ mkdir -p src/views
  5. Créez deux fichiers : app.mjs et exp.mjs – dans le dossier src. Notez que l'extension du fichier est .mjs et pas seulement .js, ce qui indique que vous utilisez des modules ECMA.

    $ touch app.mjs$ touch exp.mjs$ ls -1 srcapp.mjsexp.mjs
  6. Ensuite, ajoutez les dépendances axios, ejs et express :

    $ npm install axios ejs express --save
  7. Ajoutez ce qui suit dans le fichier exp.mjs :

    import express from "express"export const exp = await express();

Notez que cela utilise le mot-clé await sans le mot-clé async. Cette attente attend que l'instance express s'initialise avant de l'exporter vers d'autres modules. Vous pouvez utiliser cet exemple pour attendre l'initialisation des instances avant de passer au code qui dépend de la ressource attendue.

Si un module contient une attente de niveau supérieur, alors l'exécution de ce module et du module parent s'arrêtera jusqu'à ce que la promesse soit résolue. Tous les frères et sœurs continueront à s'exécuter de la manière habituelle et synchrone.

Notez que le chargement du module dans Node.js est également synchrone, ce qui signifie qu'il n'attend pas le chargement d'une ressource. Vous obtiendrez une erreur. Cependant, vous pouvez obtenir un comportement asynchrone en plaçant le mot-clé await devant la ressource qui charge ou effectue un traitement.

Ajouter les API d'actualités

Cette application utilise deux API d'actualités disponibles gratuitement pour obtenir des données. L'utilisation de deux API prend en charge le comportement de dépendance de secours ; si une API ne parvient pas à récupérer les données, l'autre les obtiendra. Ces deux API utilisent des clés API :

  • API d'actualités
  • API GNews

Insérez le code suivant dans le fichier app.mjs. Cette première section importe les instances axios et express initialisées dans exp.js. La partie suivante configure le moteur d'affichage pour afficher les fichiers .ejs dans un navigateur :

import { exp } from "./exp.mjs";
import axios from "axios"

exp.set("view engine","ejs");
// dependency fall back
let response = "";
let site = true;
try{
   response = await axios('https://newsapi.org/v2/top-headlines?country=us&apiKey=your-api-key');  
 }
 catch{
  response = await axios("https://gnews.io/api/v3/top-news?token=your-api-key"); 
  site = false; 
 }
 // Get top news
exp.get('/',function(req,res){
  let response0 = response.data.articles  
  res.render('main.ejs',{response0: response0, site:site})
 })
 // search news
exp.get('/search', function(req,res){
  res.render("searchnews.ejs")   
})
exp.get('/result', async(req, res)=>{
  let x = req.query.newtitlesearch;
  let response1 = {}
  let data = {}
  try{
    let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'
    response1 =  await axios(url);
  }
  catch{
    let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'
    response1 =  await axios(url)
  }
  res.render('result.ejs', {response1: response1.data.articles, site: site})  
})
exp.listen(3000)

La partie la plus importante est la suivante : les blocs try et catch, qui utilisent wait de niveau supérieur pour attendre qu'axios récupère les données de l'API et, si pour une raison quelconque , axios ne parvient pas à récupérer les données de la première API, l'application utilise la deuxième API pour obtenir les données. Une fois qu'il obtient les données de l'API, express les restitue sur la page principale :

try{
   response = await axios('https://newsapi.org/v2/top-headlines?country=us&apiKey=your-api-key');
  
 }
 catch{
  response = await axios("https://gnews.io/api/v3/top-news?token=your-api-key");
  
 }

Ensuite, il y a un autre itinéraire express qui amène les utilisateurs vers un formulaire de recherche où ils peuvent rechercher les actualités qui les intéressent :

// search news
exp.get('/search', function(req,res){
  res.render("../src/view/searchnews.ejs")  
})

Enfin, un autre itinéraire express affiche les résultats de la recherche :

exp.get('/result', async(req,res)=>{
  let x = req.query.newtitlesearch;
  let response1 = {}
  let data = {}
  try{
    let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'
    response1 =  await axios(url);
  }
  catch{
    let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'
    response1 =  await axios(url)
  }
  res.render('../src/view/result.ejs', {response1: response1.data.articles , site: site})  
})

Écrire les pages frontend

La dernière partie de l'application écrit les quatre fichiers HTML .ejs pour les pages frontales. Enregistrez ces fichiers dans le dossier views :

//header.ejs
<!DOCTYPE html>
<head>
    <title>newapiapp</title>
    <link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
</head>

<body>  
    <nav class="navbar navbar-default">
              <div class="container-fluid">
                  <div class="navbar-header">
                      <a class="navbar-brand" href="#">News app</a>
                  </div> 
                  <div class="collapse navbar-collapse">
                      <ul class="nav navbar-nav navbar-right">
                             <li><a href="https://opensource.com/">Main</a></li>                
                              <li><a href="https://opensource.com/search">Search</a></li>      
                      </ul>      
                  </div>
              </div>  
    </nav>
//main.ejs
<%- include('header');%>
<%let rows = response0%>
<%let siterep = site%>

<div name "container">
  <div class="row text-center" style="display:flex; flex-wrap:wrap">
    <% for(let i = 0; i < rows.length; i++){%>
      <div class="col-md-3 col-sm-6 ">
                          <div class="thumbnail" >
                            <a href="https://opensource.com/%3C%25-rows%5Bi%5D.url%20%25%3E">
                              <img src = "<%= siterep ? rows[i].urlToImage :  rows[i].url  %>">
                            </a>                            
                          </div>
                          <div><%= rows[i].title%></div>                 
                        </div>    
    <% } %>
  </div>  
</div>
//Searchnews.ejs 
<%- include('header');%>

  <h1>Search news </h1>
  <form action="https://opensource.com/result" method="Get">
      <iput type ="text" placeholder="news title search" name="newtitlesearch"></input>
        <input type="submit" placeholder="submit"></input>        
   </form>
//result.ejs
<%- include('header');%>
<%let rows = response1%>
<%let siterep = site%>

<div name "container">
  <div class="row text-center" style="display:flex; flex-wrap:wrap">

    <% for(let i = 0; i < rows.length; i++){%>
      <div class="col-md-3 col-sm-6 ">
                          <div class="thumbnail" >
                            <a href="https://opensource.com/%3C%25-rows%5Bi%5D.url%20%25%3E">
                              <img src = "<%= siterep ? rows[i].urlToImage :  rows[i].url  %>">
                            </a>                            
                          </div>
                          <div><%= rows[i].title%></div>                  
                        </div>    
    <% } %>
  </div>  
</div>

Exécutez l'application

L'application est désormais terminée et vous pouvez l'essayer.

Si vous utilisez une version Node.js de la v13.3 à la v14.0, exécutez le programme avec :

$ node --harmony-top-level-await app.js

Si vous utilisez Node.js v14 et versions ultérieures, vous n'avez pas besoin d'utiliser l'indicateur --harmony de la V8 :

$ node app.js

Si vous avez réussi à créer cette application, félicitations ! Vous avez appris une nouvelle fonctionnalité JavaScript.

Vous pouvez trouver d'autres utilisations de l'attente de niveau supérieur dans la proposition d'attente de niveau supérieur ECMAScript TC39.

Le code de cet exemple d'attente de niveau supérieur utilisant Node.js par Sumaira Ahmad est open source sous licence CC BY 4.0.

Articles connexes: