web-dev-qa-db-fra.com

Renvoyer une valeur de la fonction de rappel dans Node.js

Je rencontre un petit problème pour renvoyer une valeur de la fonction de rappel dans Node.js, je vais essayer d’expliquer ma situation aussi facilement que possible. Considérez que j'ai un extrait, qui prend l'URL et frappe cette URL et donne la sortie:

urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {                              
    var statusCode = response.statusCode;
    finalData = getResponseJson(statusCode, data.toString());
});

J'ai essayé de l'envelopper dans une fonction et de renvoyer une valeur comme celle-ci:

function doCall(urlToCall) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {                              
    var statusCode = response.statusCode;
    finalData = getResponseJson(statusCode, data.toString());
    return finalData;
});
}

Parce que dans mon code Node.js, j'ai beaucoup d'instructions if-else où la valeur de urlToCall sera décidée, comme ceci:

if(//somecondition) {
   urlToCall = //Url1;
} else if(//someother condition) {
   urlToCall = //Url2;
} else {
   urlToCall = //Url3;
}

La chose est que toutes les déclarations à l'intérieur d'un urllib.request resteront les mêmes, à l'exception de la valeur de urlToCall. Donc, définitivement, j'ai besoin de mettre ces codes communs dans une fonction. J'ai essayé la même chose mais dans doCall me rendra toujours undefined. J'ai essayé comme ça:

response = doCall(urlToCall);
console.log(response) //Prints undefined

Mais si j'imprime une valeur dans doCall(), elle s'imprime parfaitement, mais elle renvoie toujours undefined. Selon mes recherches, j'ai appris que nous ne pouvons pas renvoyer de valeurs des fonctions de rappel! (est-ce vrai)? Dans l'affirmative, quelqu'un peut-il me conseiller sur la façon de gérer cette situation, car je souhaite éviter la duplication de code dans tous les blocs if-else.

66
Pradeep Simha

Son undefined parce que console.log(response) s'exécute avant que doCall(urlToCall); ne soit terminé. Vous devez également transmettre une fonction de rappel, qui s'exécute lorsque votre demande est terminée.

Tout d'abord, votre fonction. Passez un rappel:

function doCall(urlToCall, callback) {
    urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {                              
        var statusCode = response.statusCode;
        finalData = getResponseJson(statusCode, data.toString());
        return callback(finalData);
    });
}

Maintenant:

var urlToCall = "http://myUrlToCall";
doCall(urlToCall, function(response){
    // Here you have access to your variable
    console.log(response);
})

@Rodrigo, a posté un bonne ressource dans les commentaires. Lisez à propos de rappels dans le noeud et comment ils fonctionnent. Rappelez-vous, c'est du code asynchrone.

110
aludvigsen

Je rencontre un petit problème pour renvoyer une valeur de la fonction de rappel dans Node.js

Ce n'est pas un "petit problème", il est en fait impossible de "renvoyer" une valeur au sens traditionnel à partir d'une fonction asynchrone.

Comme vous ne pouvez pas "renvoyer la valeur", vous devez appeler la fonction qui en aura besoin une fois que vous l'avez. @display_name a déjà répondu à votre question, mais je voulais simplement souligner que le retour dans doCall est ne renvoie pas la valeur de la manière traditionnelle. Vous pouvez écrire doCall comme suit:

function doCall(urlToCall, callback) {
    urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {                              
        var statusCode = response.statusCode;
        finalData = getResponseJson(statusCode, data.toString());
        // call the function that needs the value
        callback(finalData);
        // we are done
        return;
    });
}

La ligne callback(finalData); est ce qui appelle la fonction qui a besoin de la valeur obtenue de la fonction async. Mais sachez que l'instruction return est utilisée pour indiquer que la fonction se termine ici , mais cela ne signifie pas que la valeur est renvoyée à l'appelant (l'appelant est déjà passé).

14
Hector Correa

Exemple de code pour node.js - fonction asynchrone à fonction sync:

var deasync = require('deasync');

function syncFunc()
{
    var ret = null;
    asyncFunc(function(err, result){
        ret = {err : err, result : result}
    });

    while((ret == null))
    {
         deasync.runLoopOnce();
    }

    return (ret.err || ret.result);
}
2
Dawid Szymański

Si ce que vous voulez, c'est que votre code fonctionne sans trop modifier. Vous pouvez essayer cette solution qui supprime les rappels et conserve le même flux de travail de code :

Étant donné que vous utilisez Node.js, vous pouvez utiliser co et co-requête pour atteindre le même objectif sans souci de rappel.

En gros, vous pouvez faire quelque chose comme ça:

function doCall(urlToCall) {
  return co(function *(){
    var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.                             
    var statusCode = response.statusCode;
    finalData = getResponseJson(statusCode, data.toString());
    return finalData;
  });
}

Ensuite,

var response = yield doCall(urlToCall); // "yield" garuantees the callback finished.
console.log(response) // The response will not be undefined anymore.

En faisant cela, nous attendons que la fonction de rappel se termine, puis nous en récupérons la valeur. D'une certaine manière, cela résout votre problème.

2
Ethan Yang