node.js a rendu faciles les serveurs hautement concurrents en faisant tout passer par des E/S non bloquantes, et le coût est apparu dans la structure : chaque requête de base de données ou appel d'API ajoutait un niveau de callback, et les propres projets de koush, conçus pour maintenir des dizaines de milliers de connexions de longue durée, atteignaient régulièrement sept ou huit niveaux d'indentation. Une simple requête suivie d'une interrogation nécessitait déjà deux callbacks imbriqués, la gestion des erreurs enveloppait chaque niveau dans son propre try/catch, et le travail sérialisé comme les appels d'API paginés se transformait en fonctions de continuation récursives.
Les correctifs disponibles en 2011 déplaçaient le problème plutôt que de le supprimer : l'opérateur yield suspendait et reprenait les itérateurs mais restait peu pratique, et les traducteurs source-à-source comme TameJS compilaient du code d'apparence propre en JavaScript ordinaire exécutable partout, au prix de déboguer le code généré plutôt que le code écrit par le développeur. Le point de référence de Koush était async/await de C# 5, une fonctionnalité de langage explicite qui permettait au code asynchrone de se lire de façon synchrone.
Son geste fut d'intégrer cette fonctionnalité dans le runtime : un fork de node.js avec des coroutines implémentées dans V8, un marqueur de fonction async, et un await qui termine la fonction et la reprend lorsque le callback se termine. Le parseur générait encore à peu près le même AST que la version à callbacks, de sorte qu'un seul try/catch couvrait chaque await et qu'une boucle while pouvait sérialiser des requêtes d'API paginées, tandis que ndb et node-inspector pouvaient toujours déboguer le code d'origine. Le prix était une compilation personnalisée de node, qu'une équipe contrôlant ses propres serveurs pouvait raisonnablement payer.
Suspendre la fonction sur await via les coroutines V8 pour que le code asynchrone se lise comme du code synchrone
Les callbacks imbriqués se multiplient à chaque étape d'E/S et repoussent la gestion des erreurs dans des portées séparées ; await maintient un seul try/catch autour de tout un flux, de sorte que les exceptions traversent les frontières de callbacks comme dans du code synchrone.
De vraies coroutines dans le runtime, et non une traduction source-à-source, font que le code écrit par le développeur est le code qui s'exécute, si bien que ndb et node-inspector déboguent l'original plutôt que la sortie générée.
Un flux de contrôle d'apparence séquentielle permet à des boucles ordinaires d'exprimer un travail asynchrone sérialisé : l'exemple GDATA paginé remplace une fonction récursive de traitement par morceaux par une boucle while.
Comme une équipe serveur contrôle son déploiement, forker node.js et V8 était un prix réaliste, et la conception a été livrée comme une branche fonctionnelle avec des tests plutôt que comme une proposition.
Lorsque des contournements poussent la complexité dans du code généré ou dans un imbrication plus profonde, corriger au niveau du langage : suspendre toute la fonction sur les E/S pour que le code lisible soit ce qui s'exécute et ce que l'on débogue
Suites
Le projet est resté un fork plutôt qu'une fonctionnalité fusionnée : le wiki, modifié pour la dernière fois en avril 2013 après 25 révisions, présentait une compilation personnalisée de node comme une option de déploiement, et sa TODO listait l'optimisation des branches contenant await. Publié sur Hacker News le 29 septembre 2011, il a recueilli 222 points et 101 commentaires ; les lecteurs ont qualifié l'approche de très élégante et ont convenu que le correctif approprié était d'introduire des coroutines. Rien dans les sources ne montre que le patch soit entré dans node.js ; son effet visible a été de démontrer, avec du code fonctionnel, comment JavaScript pouvait obtenir async/await au niveau du langage.
VÉRIFIEZ LES SOURCES
Les sources
- "async" support in node.js (koush/node wiki) GitHub — koush/node wiki · github.com
- Fixing the callback spaghetti in node.js | Hacker News Hacker News · news.ycombinator.com ·