296 votes

Comment faire attendre une fonction jusqu'à ce qu'un callback ait été appelé en utilisant node.js

J'ai une fonction simplifiée qui ressemble à ceci :

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}

En gros, je veux qu'il appelle myApi.exec et renvoie la réponse qui est donnée dans le lambda de rappel. Cependant, le code ci-dessus ne fonctionne pas et renvoie simplement immédiatement.

Pour une tentative très artisanale, j'ai essayé ce qui suit qui n'a pas fonctionné, mais au moins vous avez l'idée de ce que j'essaie de faire :

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  while (!r) {}
  return r;
}

En gros, quelle est la meilleure façon de procéder pour un nœud.js/événement ? Je veux que ma fonction attende que le callback soit appelé, puis retourne la valeur qui lui a été passée.

4 votes

Ou est-ce que je ne m'y prends pas du tout de la bonne manière, et devrais-je appeler un autre callback, plutôt que de renvoyer une réponse ?

0 votes

Ce site est à mon avis la meilleure explication du SO pourquoi la boucle occupée ne fonctionne pas.

0 votes

N'essayez pas d'attendre. Appelez simplement la fonction suivante (dépendante de la callback) à la fin de la callback elle-même.

297voto

Jakob Points 11155

La "bonne méthode node.js/event driven" pour faire cela est de ne pas attendre .

Comme presque tout lorsque vous travaillez avec des systèmes événementiels comme node, votre fonction doit accepter un paramètre de rappel qui sera invoqué lorsque le calcul sera terminé. L'appelant ne doit pas attendre que la valeur soit "retournée" au sens normal, mais plutôt envoyer la routine qui traitera la valeur résultante :

function(query, callback) {
  myApi.exec('SomeCommand', function(response) {
    // other stuff here...
    // bla bla..
    callback(response); // this will "return" your value to the original caller
  });
}

Donc tu ne l'utilises pas comme ça :

var returnValue = myFunction(query);

Mais comme ça :

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});

5 votes

Ok super. Et si myApi.exec n'appelait jamais le callback ? Comment puis-je faire en sorte que le callback soit appelé après disons 10 secondes avec une valeur d'erreur disant que le temps est écoulé ou quelque chose comme ça ?

5 votes

Ou mieux encore (ajout d'une vérification pour que le callback ne soit pas invoqué deux fois) : jsfiddle.net/LdaFw/1

169 votes

Il est clair que le non-blocage est la norme dans node/js, mais il y a certainement des moments où le blocage est souhaité (par exemple, le blocage sur stdin). Même node a des méthodes "bloquantes" (voir toutes les méthodes de fs sync* méthodes). En tant que tel, je pense que c'est toujours une question valide. Existe-t-il un moyen agréable de réaliser le blocage dans un nœud, à part l'attente occupée ?

24voto

Lucio M. Tato Points 827

Vérifiez ça : https://github.com/luciotato/waitfor-ES6

votre code avec wait.for : (nécessite des générateurs, drapeau --harmony)

function* (query) {
  var r = yield wait.for( myApi.exec, 'SomeCommand');
  return r;
}

5voto

Albert Points 12642

Note : Cette réponse ne devrait probablement pas être utilisée dans un code de production. C'est un hack et vous devez connaître les implications.

Il y a le uvrun (mis à jour pour les nouvelles versions de Nodejs) aquí ) où vous pouvez exécuter un tour de boucle unique de la boucle d'événement principale de la libuv (qui est la boucle principale de Nodejs).

Votre code ressemblerait à ceci :

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}

(Vous pouvez aussi utiliser uvrun.runNoWait() . Cela pourrait éviter certains problèmes de blocage, mais prend 100% du CPU).

Notez que cette approche invalide en quelque sorte l'objectif de Nodejs, à savoir que tout soit asynchrone et non bloquant. De plus, cela pourrait augmenter considérablement la profondeur de votre pile d'appels, et vous pourriez vous retrouver avec des débordements de pile. Si vous exécutez une telle fonction de manière récursive, vous aurez certainement des problèmes.

Voir les autres réponses sur la façon de reconcevoir votre code pour le faire "correctement".

Cette solution n'est probablement utile que lorsque vous effectuez des tests et que vous voulez surtout avoir un code synchronisé et sériel.

1voto

Z0LtaR Points 11

Supposons que vous ayez une fonction :

var fetchPage(page, callback) {
   ....
   request(uri, function (error, response, body) {
        ....
        if (something_good) {
          callback(true, page+1);
        } else {
          callback(false);
        }
        .....
   });

};

vous pouvez utiliser des callbacks comme ceci :

fetchPage(1, x = function(next, page) {
if (next) {
    console.log("^^^ CALLBACK -->  fetchPage: " + page);
    fetchPage(page, x);
}
});

0voto

rfw Points 3716

Cela va à l'encontre du but de l'IO non-bloquante -- vous la bloquez alors qu'elle n'a pas besoin d'être bloquée. :)

Vous devriez imbriquer vos callbacks au lieu de forcer node.js à attendre, ou appeler un autre callback à l'intérieur du callback où vous avez besoin du résultat de r .

Il y a de fortes chances que si vous devez forcer le blocage, c'est que vous pensez mal à votre architecture.

0 votes

Je me doutais que j'avais ça à l'envers.

31 votes

Il y a de fortes chances que je veuille simplement écrire un script rapide pour http.get() une certaine URL et console.log() son contenu. Pourquoi dois-je faire un saut en arrière pour faire cela dans Node ?

6 votes

@DanDascalescu : Et pourquoi dois-je déclarer des signatures de type pour le faire dans les langages statiques ? Et pourquoi dois-je le mettre dans une méthode principale dans les langages de type C ? Et pourquoi dois-je le compiler dans un langage compilé ? Ce que vous remettez en question est une décision de conception fondamentale dans Node.js. Cette décision a des avantages et des inconvénients. Si vous ne l'aimez pas, vous pouvez utiliser un autre langage qui correspond mieux à votre style. C'est pourquoi nous en avons plus d'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X