Je veux créer une page qui affiche certaines données d'une base de données, j'ai donc créé des fonctions qui récupèrent ces données de ma base de données. Je ne suis qu'un débutant dans Node.js, donc pour autant que je sache, si je veux tous les utiliser dans une seule page (réponse HTTP), je devrais tous les imbriquer:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = "<h1>Demo page</h1>";
getSomeDate(client, function(someData) {
html += "<p>"+ someData +"</p>";
getSomeOtherDate(client, function(someOtherData) {
html += "<p>"+ someOtherData +"</p>";
getMoreData(client, function(moreData) {
html += "<p>"+ moreData +"</p>";
res.write(html);
res.end();
});
});
});
S'il y a beaucoup de fonctions comme ça, alors l'imbrication devient un problème .
Y a-t-il un moyen d'éviter cela? Je suppose que cela a à voir avec la façon dont vous combinez plusieurs fonctions asynchrones, ce qui semble être quelque chose de fondamental.
getSomeDate
etgetSomeOtherDate
finit par changer l'indentation de nombreuses lignes, ce qui rend l'historique de git plus difficile à lire (git blame
est même inutile après cela), et vous faites probablement des bugs en faisant cela manuellementRéponses:
Observation intéressante. Notez qu'en JavaScript, vous pouvez normalement remplacer les fonctions de rappel anonyme en ligne par des variables de fonction nommées.
Le suivant:
Peut être réécrit pour ressembler à ceci:
Cependant, à moins que vous ne prévoyiez de réutiliser la logique de rappel à d'autres endroits, il est souvent beaucoup plus facile de lire des fonctions anonymes en ligne, comme dans votre exemple. Cela vous évitera également d'avoir à trouver un nom pour tous les rappels.
De plus, notez que comme @pst l'a noté dans un commentaire ci-dessous, si vous accédez à des variables de fermeture dans les fonctions internes, ce qui précède ne serait pas une traduction simple. Dans de tels cas, l'utilisation de fonctions anonymes en ligne est encore plus préférable.
la source
getMoreData
est perdu.someDataParser
analyse en fait TOUTES les données, car il appelle égalementgetMoreData
. En ce sens, le nom de la fonction est incorrect et il devient évident que nous n'avons pas réellement supprimé le problème d'imbrication.Kay, utilisez simplement l'un de ces modules.
Cela transformera ceci:
Dans ceci:
la source
Pour la plupart, je suis d'accord avec Daniel Vassallo. Si vous pouvez diviser une fonction compliquée et profondément imbriquée en fonctions nommées distinctes, c'est généralement une bonne idée. Pour les moments où cela a du sens de le faire dans une seule fonction, vous pouvez utiliser l'une des nombreuses bibliothèques asynchrones node.js disponibles. Les gens ont trouvé de nombreuses façons différentes de résoudre ce problème, alors jetez un œil à la page des modules node.js et voyez ce que vous en pensez.
J'ai écrit un module pour cela moi-même, appelé async.js . En utilisant cela, l'exemple ci-dessus pourrait être mis à jour pour:
Une bonne chose à propos de cette approche est que vous pouvez rapidement changer votre code pour récupérer les données en parallèle en changeant la fonction «série» en «parallèle». De plus, async.js fonctionnera également dans le navigateur, vous pouvez donc utiliser les mêmes méthodes que vous le feriez dans node.js si vous rencontrez un code asynchrone délicat.
J'espère que c'est utile!
la source
Vous pouvez utiliser cette astuce avec un tableau plutôt que des fonctions imbriquées ou un module.
Beaucoup plus facile pour les yeux.
Vous pouvez étendre l'idiome pour des processus parallèles ou même des chaînes de processus parallèles:
la source
J'aime beaucoup async.js à cet effet.
Le problème est résolu par la commande waterfall:
Exemple
Quant aux variables req, res, elles seront partagées dans la même portée que function (req, res) {} qui renfermait tout l'appel async.waterfall.
Non seulement ainsi, async est très propre. Ce que je veux dire, c'est que je change beaucoup de cas comme celui-ci:
Au premier:
Puis à ceci:
Puis à ceci:
Il permet également d'appeler très rapidement de nombreuses fonctions prédéfinies préparées pour async depuis util.js. Enchaînez simplement ce que vous voulez faire, assurez-vous que o, cb est universellement géré. Cela accélère beaucoup l'ensemble du processus de codage.
la source
Il vous faut un peu de sucre syntaxique. Vérifiez ceci:
Assez bien , n'est-ce pas? Vous remarquerez peut-être que html est devenu un tableau. C'est en partie parce que les chaînes sont immuables, il est donc préférable de mettre en mémoire tampon votre sortie dans un tableau, que de supprimer des chaînes de plus en plus grandes. L'autre raison est à cause d'une autre syntaxe intéressante avec
bind
.Queue
dans l'exemple n'est en réalité qu'un exemple etpartial
peut être implémenté comme suitla source
last
fonction optionnelle )obj.email
et votre fonction suivante utiliseobj.email
puis la supprime (ou simplement l'assignenull
).Je suis amoureux d' Async.js depuis que je l'ai trouvé. Il a une
async.series
fonction que vous pouvez utiliser pour éviter une longue imbrication.Documentation:-
série (tâches, [rappel])
Exécutez un tableau de fonctions en série, chacune s'exécutant une fois la fonction précédente terminée. [...]
Arguments
tasks
- Un tableau de fonctions à exécuter, chaque fonction reçoit un rappel qu'elle doit appeler à la fin.callback(err, [results])
- Un rappel optionnel à exécuter une fois que toutes les fonctions sont terminées. Cette fonction obtient un tableau de tous les arguments passés aux callbacks utilisés dans le tableau.Voici comment nous pouvons l'appliquer à votre exemple de code: -
la source
Le sucre syntaxique le plus simple que j'ai vu est la promesse de nœud.
npm install node-promise || git clone https://github.com/kriszyp/node-promise
En utilisant cela, vous pouvez enchaîner les méthodes asynchrones comme:
La valeur de retour de chacun est disponible comme argument dans le suivant.
la source
Ce que vous avez fait là-bas, c'est de prendre un modèle asynchrone et de l'appliquer à 3 fonctions appelées en séquence, chacune attendant que la précédente se termine avant de commencer - c'est-à-dire que vous les avez rendues synchrones . Le point sur la programmation asynchrone est que vous pouvez exécuter plusieurs fonctions toutes en même temps et ne pas avoir à attendre qu'elles soient terminées.
si getSomeDate () ne fournit rien à getSomeOtherDate (), qui ne fournit rien à getMoreData () alors pourquoi ne les appelez-vous pas de manière asynchrone comme js le permet ou s'ils sont interdépendants (et non asynchrones), écrivez-les comme un fonction unique?
Vous n'avez pas besoin d'utiliser l'imbrication pour contrôler le flux - par exemple, terminez chaque fonction en appelant une fonction commune qui détermine quand les 3 sont terminées, puis envoie la réponse.
la source
Supposons que vous puissiez faire ceci:
Il vous suffit d'implémenter chain () pour qu'il applique partiellement chaque fonction à la suivante et n'appelle immédiatement que la première fonction:
la source
L'enfer des rappels peut être facilement évité en javascript pur avec fermeture. la solution ci-dessous suppose que tous les rappels suivent la signature de la fonction (erreur, données).
la source
J'ai récemment créé une abstraction plus simple appelée wait.for pour appeler les fonctions asynchrones en mode sync (basé sur Fibers). C'est à un stade précoce mais ça marche. C'est a:
https://github.com/luciotato/waitfor
En utilisant wait.for , vous pouvez appeler n'importe quelle fonction async standard de nodejs, comme s'il s'agissait d'une fonction de synchronisation.
en utilisant wait.for votre code pourrait être:
... ou si vous voulez être moins verbeux (et ajouter également une détection d'erreur)
Dans tous les cas, getSomeDate , getSomeOtherDate et getMoreData doivent être des fonctions asynchrones standard avec le dernier paramètre un rappel de fonction (err, data)
un péché:
la source
Pour résoudre ce problème, j'ai écrit nodent ( https://npmjs.org/package/nodent ) qui pré-traite votre JS de manière invisible. Votre exemple de code deviendrait (asynchrone, vraiment - lisez la documentation).
De toute évidence, il existe de nombreuses autres solutions, mais le prétraitement a l'avantage d'avoir peu ou pas de temps d'exécution et grâce à la prise en charge de la carte source, il est également facile à déboguer.
la source
J'ai eu le même problème. J'ai vu les principales bibliothèques à nœuds exécuter des fonctions asynchrones, et elles présentent un chaînage si non naturel (vous devez utiliser trois méthodes ou plus confs, etc.) pour construire votre code.
J'ai passé quelques semaines à développer une solution simple et facile à lire. S'il vous plaît, essayez EnqJS . Toutes les opinions seront appréciées.
Au lieu de:
avec EnqJS:
Observez que le code semble être plus gros qu'avant. Mais ce n'est pas imbriqué comme avant. Pour paraître plus naturelles, les chaînes sont appelées immédiatement:
Et pour dire qu'il est revenu, à l'intérieur de la fonction que nous appelons:
la source
Je le fais d'une manière assez primitive mais efficace. Par exemple, je dois obtenir un modèle avec ses parents et ses enfants et disons que je dois faire des requêtes séparées pour eux:
la source
Utilisez Fibers https://github.com/laverdet/node-fibers, cela rend le code asynchrone synchrone (sans blocage)
J'utilise personnellement ce petit wrapper http://alexeypetrushin.github.com/synchronize Exemple de code de mon projet (chaque méthode est en fait asynchrone, travaillant avec un fichier asynchrone IO) J'ai même peur d'imaginer quel désordre ce serait avec le rappel ou bibliothèques d'assistance async-control-flow.
la source
Task.js vous propose ceci:
Au lieu de cela:
la source
Après que les autres ont répondu, vous avez déclaré que votre problème était des variables locales. Il semble qu'un moyen facile de le faire est d'écrire une fonction externe pour contenir ces variables locales, puis d'utiliser un tas de fonctions internes nommées et d'y accéder par leur nom. De cette façon, vous n'en nicherez que deux en profondeur, quel que soit le nombre de fonctions dont vous avez besoin pour enchaîner.
Voici la tentative de mon débutant d'utiliser le
mysql
module Node.js avec l'imbrication:Ce qui suit est une réécriture utilisant des fonctions internes nommées. La fonction externe
with_connection
peut également être utilisée comme support pour les variables locales. (Ici, j'ai les paramètressql
,bindings
,cb
qui agissent de la même manière, mais vous pouvez définir quelques - unes des variables locales supplémentaireswith_connection
.)J'avais pensé qu'il serait peut-être possible de créer un objet avec des variables d'instance et d'utiliser ces variables d'instance en remplacement des variables locales. Mais maintenant, je trouve que l'approche ci-dessus utilisant des fonctions imbriquées et des variables locales est plus simple et plus facile à comprendre. Il faut du temps pour désapprendre OO, il semble :-)
Voici donc ma version précédente avec un objet et des variables d'instance.
Il s'avère que cela
bind
peut être utilisé à un certain avantage. Cela me permet de me débarrasser des fonctions anonymes quelque peu laides que j'ai créées et qui n'ont rien fait, sauf pour se transmettre à un appel de méthode. Je n'ai pas pu passer la méthode directement car elle aurait été impliquée avec la mauvaise valeur dethis
. Mais avecbind
, je peux spécifier la valeur de cethis
que je veux.Bien sûr, rien de tout cela n'est correct JS avec le codage Node.js - je viens de passer quelques heures dessus. Mais peut-être qu'avec un peu de polissage, cette technique peut vous aider?
la source
async.js fonctionne bien pour cela. Je suis tombé sur cet article très utile qui explique la nécessité et l'utilisation de async.js avec des exemples: http://www.sebastianseilund.com/nodejs-async-in-practice
la source
Si vous ne voulez pas utiliser "step" ou "seq", essayez "line" qui est une fonction simple pour réduire les rappels asynchrones imbriqués.
https://github.com/kevin0571/node-line
la source
Asyncawait comme C # est une autre façon de faire
https://github.com/yortus/asyncawait
la source
En utilisant le fil, votre code ressemblerait à ceci:
la source
pour votre connaissance, pensez à Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase
la source