JavaScript évolue

Le comité TC39, responsable de la standardisation du langage JavaScript, a annoncé des avancées significatives lors de sa 108e réunion. Trois propositions majeures sont sur le point d'être intégrées à la spécification ECMAScript, promettant de simplifier le code asynchrone, de fiabiliser la détection d'erreurs et d'optimiser la gestion des ressources système.

La communauté des développeurs JavaScript a suivi avec attention la dernière réunion du comité TC39, qui s'est tenue à La Corogne, en Espagne. Les délibérations ont abouti à l'avancement de neuf propositions, dont trois ont atteint le "Stade 4", l'étape finale avant leur inclusion officielle dans le standard du langage. Ces nouveautés, attendues de longue date, vont enrichir la boîte à outils des développeurs et standardiser des pratiques jusqu'ici gérées par des bibliothèques externes ou des solutions de contournement. L'arrivée de ces fonctionnalités natives témoigne de la maturité continue de JavaScript en tant que langage capable de gérer des applications complexes et robustes.

Les propositions atteignant le stade 4

Le passage au stade 4 signifie que ces fonctionnalités sont considérées comme stables, entièrement spécifiées et prêtes à être implémentées nativement par les navigateurs et les environnements d'exécution comme Node.js.

Leur intégration met fin aux divergences d'implémentation et offre une base solide et performante pour tous.

1. Array.fromAsync : la fin des boucles for await...of manuelles

Longtemps espérée, la méthode Array.fromAsync a été validée (sous condition d'une dernière relecture par les éditeurs).

Portée par J. S. Choi, cette nouvelle fonction utilitaire permet de transformer des itérables asynchrones (comme des flux de données ou des résultats de pagination d'API) en un tableau (Array).

Jusqu'à présent, pour collecter les valeurs d'un générateur asynchrone ou d'un flux, les développeurs devaient utiliser une boucle for await...of explicite :

// Avant
async function getItems() {
  const results = [];
  const asyncIterable = fetchPaginatedData();
  for await (const value of asyncIterable) {
    results.push(value);
  }
  return results;
}

Avec Array.fromAsync, ce processus est simplifié en une seule ligne de code concise et lisible, à l'image de son homologue synchrone Array.from :

// Après
async function getItems() {
  const asyncIterable = fetchPaginatedData();
  const results = await Array.fromAsync(asyncIterable);
  return results;
}

Cette fonctionnalité, déjà popularisée par des bibliothèques comme it-all, deviendra bientôt une capacité native de JavaScript, réduisant le code redondant et améliorant la clarté des intentions.

2. Error.isError : une vérification d'erreur enfin fiable

La proposition Error.isError, défendue par Jordan Harband après près d'une décennie de discussions, a également atteint le Stade 4.

Elle introduit une méthode standard et fiable pour déterminer si une valeur donnée est bien un objet Error.

Ce nouvel outil résout un problème persistant en JavaScript : la difficulté de vérifier la nature d'une erreur lorsque celle-ci provient d'un autre "royaume" (un contexte d'exécution distinct, comme une iframe, un Web Worker ou une machine virtuelle Node.js).

Dans ces cas, instanceof Error retourne false de manière incorrecte car l'objet erreur hérite d'un prototype Error différent de celui du contexte principal.

Error.isError contourne ce problème en vérifiant une propriété interne de l'objet, garantissant un résultat fiable quel que soit son origine.

Cette avancée est cruciale pour les bibliothèques de monitoring et de gestion d'erreurs qui ont besoin d'inspecter des erreurs de sources variées.

3. Gestion explicite des ressources avec using

La proposition sur la gestion explicite des ressources, menée par Ron Buckton, a été conditionnellement approuvée pour le Stade 4.

Elle introduit deux nouvelles déclarations, using et await using, ainsi que deux nouveaux symboles, Symbol.dispose et Symbol.asyncDispose.

Cet ajout s'inspire de mécanismes existants dans des langages comme C# ou Python et vise à garantir que des ressources (descripteurs de fichiers, connexions réseau, verrous) soient systématiquement et proprement libérées.

Cela prévient les fuites de ressources, qui peuvent dégrader les performances ou faire planter une application.

La déclaration using simplifie grandement le code qui, autrement, nécessiterait des blocs try...finally imbriqués et complexes, surtout lors de la gestion de plusieurs ressources.

Le nettoyage est déterministe : il est garanti, se produit dès la sortie du bloc, et ce dans l'ordre inverse de la déclaration, assurant une libération des dépendances sans erreur.

// exemple concis d’utilisation de using dans un cas courant : connexion à une base de données

class ConnectionBDD {
  constructor(config) {
    this.config = config
    console.log(`ouverture de la connexion à la base de données avec ${config.user}@${config.host}`)
  }

  async query(sql) {
    console.log(`exécution de la requête : ${sql}`)
    // ici on simule l’exécution
    await new Promise(resolve => setTimeout(resolve, 200))
    console.log(`résultats de la requête : [...]`)
  }

  async [Symbol.asyncDispose]() {
    // fermeture de connexion asynchrone
    await new Promise(resolve => setTimeout(resolve, 100))
    console.log(`fermeture de la connexion à ${this.config.host}`)
  }
}

async function traitementUtilisateurs() {
  await using (const conn = new ConnectionBDD({ user: 'admin', host: 'bd.exemple.com' })) {
    await conn.query('SELECT id, nom FROM utilisateurs WHERE actif = 1')
    console.log('traitement des données utilisateurs terminé')
  }
  // ici, la connexion a déjà été fermée de manière déterministe
  console.log('après le bloc using')
}

traitementUtilisateurs()

Autres avancées notables dans le pipeline

En plus de ces trois ajouts majeurs, d'autres propositions ont progressé :

  • Au Stade 3 : Immutable ArrayBuffer qui introduit un type de données binaire non modifiable. C'est essentiel pour le partage sécurisé de données entre threads (par exemple, entre le thread principal et un Web Worker) sans risque de corruption par des modifications concurrentes (race conditions).
  • Au Stade 2 : Math.clamp, une fonction simple mais utile pour borner une valeur entre un minimum et un maximum, couramment utilisée en graphisme ou en animation. Également au stade 2, Seeded Pseudo Random Numbers, qui permettra de générer des séquences de nombres pseudo-aléatoires reproductibles. Cette capacité est cruciale pour les tests unitaires, les simulations scientifiques ou les jeux vidéo où l'on souhaite pouvoir recréer des conditions identiques.
  • Au Stade 1 : Trois nouvelles idées entament leur parcours de standardisation : Keep Trailing Zeros pour un meilleur contrôle du formatage des nombres, important dans les applications financières ou scientifiques ; Inspector, un outil pour examiner la structure des objets ; et Random Functions qui vise à enrichir l'API Math avec de nouvelles fonctions aléatoires plus sophistiquées.

Ces évolutions témoignent de la vitalité de l'écosystème JavaScript. En standardisant des motifs de code courants et en résolvant des problèmes de longue date, le TC39 continue d'enrichir le langage pour le rendre plus puissant, plus sûr et plus agréable à utiliser pour les millions de développeurs qui s'en servent chaque jour. Les notes officielles de la réunion devraient être publiées dans les prochaines semaines.