Pièges : Zalgo

On l'a vu, gérer un appel synchrone est différent d'un appel asynchrone en termes d'ordre d'exécution et de cheminement des exceptions, ce qui nous amène à Zalgo.

Zalgo est un mème internet sur une créature démoniaque majeure, un peu comme Chtulhu, si ça vous parle davantage.

En août 2013, Isaac Schlueter, le créateur de npm, écrivait un billet de blog expliquant qu'il ne faut jamais écrire une API qui appellerait son callback tantôt en synchrone, tantôt en asynchrone.

Pour la blague, il affirmait que fournir ce genre d'API revenait à relâcher Zalgo dans la nature.

Nous avons vu que l'ordre d'exécution ainsi que la gestion des exceptions sont très différents selon qu'on appelle une API synchrone ou une API asynchrone. Une API qui est tantôt l'un tantôt l'autre forcerait notre code appelant à gérer deux approches totalement distinctes, ce qui est vite un cauchemar.

Voyons ça en pratique

Exemple de code 06-zalgo.js

Ce programme partait d'une bonne intention avec de la mise en cache (de la mémoïsation) d'appels.  C'est une cause courante de Zalgo.

La fonction tryTooHard() va chercher des infos en ligne basées sur la clé fournie, puis les mettre en cache. Son traitement est fondamentalement asynchrone, et la première fois, faute de cache, appellera bien son callback en asynchrone.

La fois suivante, le cache nous fournissant immédiatement la valeur, on va par inadvertance appeler le callback en synchrone. Zalgo !

La séquence des appels au sein du code appelant ne sera donc pas la même la première fois par rapport aux suivantes. Idem pour la gestion d’éventuelles exceptions.

(déroulés d'exemple des deux cas de figure)

Pour corriger ça, il suffit de s'assurer qu'on appelle le callback en asynchrone, même en cas de cache. Ici nous prenons une solution universelle avec un setTimeout(…, 0). Suivant ce qu'on a sous la main, on pourrait utiliser d'autres API pour ça si on le souhaitait.

Règle d'or absolue donc : une API qui est parfois asynchrone doit TOUJOURS être asynchrone.