La compréhension novice du nœud est que si je réécrire un code synchrone ou en ligne, pour utiliser des fonctions / rappels, je peux vous assurer que mon code est non bloqué. Je suis curieux de savoir comment cela fonctionne en termes de pile d'événements. L'exemple simple d'ici: Ne comprend pas le rappel - Stackoverflow est que cela bloquera: tandis que cela ne va pas: p> callback = function(post){
doSomethingWithPost(post)
}
db.query("select * from posts where id = 1",callback);
doSomethingElse();
4 Réponses :
Imaginez que vous utilisez la caisse enregistreuse dans une boulangerie. Vous gérez vos clients séquentiellement et de manière synchrone, comme celui-ci: p>
qui sera très lent. Maintenant, essayez plutôt de prendre les commandes de manière séquentielle, mais gérez vos clients de manière asynchrone: p>
Mise à jour: strong> J'ai refactore ce qui précède, il ressemble donc de manière plus étroitement un rappel. Vous, le caissier, frappera l'étape 3 immédiatement après avoir donné à la boulangerie. Vous toucherez l'étape 2.1 lorsque le boulanger vous avertit que le pain est prêt. Em> p>
De cette manière, vous livrez toujours autant de pain - vous ne pouvez vendre que de pain que votre boulanger peut cuire. Mais vous pouvez gérer vos clients de manière plus efficace, car au lieu d'attendre une commande de retour, vous commencez à gérer le prochain client. P>
Maintenant, vous pouvez faire toutes sortes de fantaisie à ce sujet et charger l'argent au départ et dire au client de prendre le pain à l'autre bout du bureau, ou quelque chose comme ça. Je pense que Starbucks sont jolis "étirés" de cette manière. Le caissier prend la commande, émet un certain nombre de demandes de choses et indique au client d'attendre que tout se trouve dans la zone de ramassage. Super-efficacité. P>
Maintenant, imaginez que votre ami commence à utiliser un autre enregistrement de caisse. Il suit votre exemple Async. Vous pouvez gérer plus de clients, encore plus rapidement! Notez que la seule chose que vous avez dû faire était de mettre votre ami là-bas et de lui donner votre flux de travail. P>
Vous et votre ami sont deux boucles d'événements à une seule-filetage en parallèle. Ceci est analogique à deux processus nœud.js prenant des demandes. Vous n'avez pas besoin de faire quelque chose de complexe à paralléliser cela, vous venez de courir une boucle d'événement de plus. P>
Ainsi, non, "les opérations en mouvement en fonctions" ne "pas automatiquement des processus enfants". Ils s'apparentent plus d'alarmes - quand cela est fini, notifiez-moi et laissez-moi prendre à ce stade, "Ce point" étant le code dans votre rappel. Mais le rappel sera toujours exécuté dans le même processus et le même thread. P>
Maintenant, Node.js exploite également un pool de threads interne pour io. Ceci est résumé loin de vous: poursuivre l'analogie de boulangerie, disons que vous avez une "pool bouleuse" de Bakers - à vous, debout au caisse enregistreuse, vous n'avez pas à savoir à ce sujet. Vous venez de leur donner la commande ("un loaf de levain") et livrez cet ordre lorsque vous êtes informé que c'est fini. Mais les Bakers font cuire au pain en parallèle, dans leur propre "boulanger". P>
Merci pour la grande analogie. Pour plus de précision. Dans cet exemple, un caissier prend une commande et le remet à la cuisine, il peut ensuite aller traiter une autre commande. Maintenant, le preneur de commande Voici la fonction de serveur principale .. La cuisine est toutes les fonctions déléguées avec leurs propres rappels. Si la fonction de serveur déléguette à une fonction et les tend un rappel et dit .. revenez-moi lorsque vous avez terminé .. Si la cuisine n'est pas donnée un moyen de frayer un processus d'enfant. Il ne bloquera-t-il pas encore jusqu'à ce que le rappel est envoyé?
La cuisine a une piscine de fil interne. Il aura donc des threads pour distribuer les tâches à. Mais ceci est résumé de vous dans Node.js, et rien de tout cela n'est exécuté dans votre boucle d'événement, de sorte que cela n'affectera pas du tout votre code. Si vous faites du calcul intensif de la CPU dans votre propre code, cela bloquera toute la boucle d'événement i> (comme, disons que le caissier décide de faire cuire l'un des loafs lui-même - pendant ce temps, il peut ' t prendre de nouvelles commandes).
Êtes-vous en train de dire que pour un nœud de fonction / rappel standard peut utiliser un pool de thread interne pour assurer des performances asynchrones, mais que pour un appel coûteux, nous aurions besoin de configurer un travailleur comme celui de l'exécutant? Si oui, comment puis-je connaître le seuil de «cher»? Cela semble être un point clé alors.
Exactement. Tant que vous utilisez des API asynchrones pour tous les IO (FS, DB, Network, etc.), vous êtes en sécurité. Mais si vous avez besoin de calculer un calcul coûteux, vous devez utiliser un processus d'enfant pour cela - ou même un scénario de piscine de travailleurs. Je ne peux pas vous donner un seuil, vous devez faire de l'analyse comparative / profilage pour découvrir des goulots d'étranglement.
@Linus, par API asynchrones Voulez-vous dire, ils ont tous leur piscine de fil interne, car seul moyen d'atteindre l'état non bloquant consiste à accumuler un nouveau fil, la question est de savoir si nous le faisons explicitement ou le cadre le fait pour nous. droite!!
Je ne suis pas bien en anglais, je ne peux donc pas comprendre ce que vous voulez dire. Mais je peux dire que "Multi thread" et "asynchrones" sont un terme similaire mais différent. Même si un seul thread peut peformant comme «asynchrone». P>
Ce document n'est pas pour le nœud (c'est pour un cadre asynchrone Python "torsadé") Mais pourrait être utile pour vous. P>
Désolé pour mon mauvais anglais. p>
C'est un excellent document que vous avez fourni, merci beaucoup! Très clair.
C'est un bon doc. Et votre anglais est bon aussi :)
L'article est précieux. Et votre anglais est génial :)
La réponse acceptée est excellente, mais j'aimerais ajouter quelque chose de purement lié à ou est-ce parce que les événements d'E / S ne font que bloquer fondamentalement
les opérations signifient qu'ils saisissent le contrôle et ne le rendent pas
jusqu'à ce que ... P>
blockQuote>
L'IO asynchrone est possible même sans un cadre fournissant son propre bassin de threads de travailleurs io. En effet, cela peut être fait sans que le système multi-filetage tant que le système sous-jacent (opérationnel) fournit un mécanisme pour ne pas bloquer IO. P>
Généralement, cela se résume à un système d'appel du système comme celui de Posix's hypothétiquement parler, si nous voyons une fonction comme Un appel SQL DB comme votre Il s'agit généralement de la manière dont les applications de serveur monopropulsifiées "jongleront" multiples connexions simultanées. P> non bloCing code>, en particulier cette partie de la question: p>
Sélectionnez CODE>
(ou Microsoft Version du même ), ou plus de variations récentes sur la même idée comme Linux Epoll code>
. P>
db.Query code> dans votre exemple, et en supposant que nous savons aussi que le cadre fournissant cette fonction pas em> s'appuie sur n'importe quel multi -threading, alors il est généralement sûr de conclure que: p>
dB.Query code> appel. li>
tandis que (vrai) code> à quelque chose comme libev li>
Sélectionnez code> ou une fonction similaire sera utilisé pour vérifier si l'une de ces demandes d'IO en attente a fini encore. LI>
dB.Query code> utilise probablement prise réseau io em> et non fichier io em>, mais de votre point de vue comme une application Les descripteurs de développeurs, de prise et de fichier sont traités de manière presque identique à de nombreux systèmes d'exploitation et peuvent tous deux être transmis à
Sélectionner CODE> sur POSIX-Aime quand même. P>
important - la db.Query () ne bloque pas la pile, car elle est exécutée ailleurs. P>
Exemple 1 blocs car la ligne 2 nécessite des informations à partir de la ligne 1 et doit donc attendre que la ligne 1 complète. La ligne 3 ne peut pas être exécutée avant la ligne 2 sous forme de code traitée par ordre de ligne, et donc la ligne 2 bloque la ligne 3 et toutes les autres lignes. P>
L'exemple 2 est non bloqué car la fonction de rappel n'est pas appelée à être exécutée jusqu'à la fin de la finition db.Quisery, il ne bloque donc pas de dossations () d'être exécutées. P>