Le nœud se ferme sans erreur et n’attend pas de promesse (rappel d’événement)

J’ai un problème vraiment étrange dans lequel attendre une promesse qui a passé sa resolve à un rappel d’émetteur d’événement quitte simplement le processus sans erreur.

 const {EventEmitter} = require('events'); async function main() { console.log("entry"); let ev = new EventEmitter(); let task = new Promise(resolve=>{ ev.once("next", function(){resolve()}); console.log("added listener"); }); await task; console.log("exit"); } main() .then(()=>console.log("exit")) .catch(console.log); process.on("uncaughtException", (e)=>console.log(e)); 

Je m’attends à ce que le processus s’interrompe lorsque j’exécute ce programme, car il est clair que “next” n’est pour l’instant jamais émis. mais le résultat obtenu est:

entrée
écouteur ajouté

Je pensais que c’était quelque chose à voir avec le ramasse-miettes, mais ev et task sont clairement toujours dans la scope de main . Je ne comprends donc vraiment pas pourquoi le processus se déroule entièrement sans erreur.

Évidemment, j’émettrais éventuellement l’événement, mais j’ai simplifié mon code à celui ci-dessus pour le reproduire. Je suis sur le node v8.7.0 . Y at-il un problème avec mon code ou s’agit-il d’un bogue de nœud?

Cette question est essentiellement la suivante: comment le nœud décide-t-il de quitter la boucle d’événements ou de s’y retourner?

En principe, le noeud conserve un nombre de références de demandes asynchrones planifiées – setTimeouts , demandes de réseaux, etc. – Chaque fois qu’une demande est planifiée, le nombre augmente, et chaque fois qu’elle est terminée, le nombre diminue. Si vous arrivez à la fin d’un cycle de boucle d’événement et que le nombre de références est égal à zéro, le noeud est terminé.

Créer simplement une promesse ou un émetteur d’événement n’augmente pas le nombre de références – la création de ces objects n’est pas réellement une opération asynchrone. Par exemple, l’état de cette promesse sera toujours en attente mais le processus se termine immédiatement:

 const p = new Promise( resolve => { if(false) resolve() }) p.then(console.log) 

Dans le même ordre d’idées, cela existe également après la création de l’émetteur et l’enregistrement d’un auditeur:

 const ev = new EventEmitter() ev.on("event", (e) => console.log("event:", e)) 

Si vous vous attendez à ce que Node attend un événement qui n’est jamais planifié, vous pouvez alors travailler avec l’idée que Node ne sait pas s’il est possible que des événements futurs soient possibles, mais parce qu’il enregistre un nombre à chaque fois qu’il est planifié.

Alors considérez cette petite modification:

 const ev = new EventEmitter() ev.on("event", (e) => console.log("event:", e)) const timer = setTimeout(() => ev.emit("event", "fired!"), 1000) // ref count is not zero, event loop will go again. // after timer fires ref count goes back to zero and node exits 

En remarque, vous pouvez supprimer la référence au temporisateur avec: timeout.unref() . Contrairement à l’exemple précédent, ceci se termine immédiatement:

 const ev = new EventEmitter() ev.on("event", (e) => console.log("event:", e)) const timer = setTimeout(() => ev.emit("event", "fired!"), 1000) timer.unref() 

Bert Belder parle de la boucle d’événements qui dissipe beaucoup d’idées fausses: https://www.youtube.com/watch?v=PNa9OMajw9w

En règle générale, votre code combine trois méthodes similaires, mais différentes: asynchrone / wait, promesses, écouteurs d’événements. Je ne suis pas sûr de ce que vous entendez par “bombes out”. Mais en regardant le code, le résultat semble attendu.

Votre processus se termine parce que vous avez appelé promettre d’append votre écouteur d’événement. Il résout avec succès et par conséquent se ferme. Si vous essayez de vous connecter tâche, cela vous donnera non défini. Au lieu de consigner “exit” dans votre déclaration then, enregistrez le résultat. La tâche sera indéfinie car le programme n’attend pas pour résoudre sa valeur et son “bloc de code est terminé”.

Vous pouvez simplifier votre code comme suit. Comme vous pouvez le voir, cela résout immédiatement puisque vous appelez la fonction de résolution.

 const { EventEmitter } = require('events'); let ev = new EventEmitter() var p = new Promise(( resolve ) => { ev.once("next", resolve("Added Event Listener")); }) p .then(res => console.log(res)) .catch(e => console.log(e))