Devolver un valor de la función de devolución de llamada en Node.js [duplicado]

91

Estoy enfrentando pequeños problemas para devolver un valor de la función de devolución de llamada en Node.js, intentaré explicar mi situación lo más fácil posible. Considere que tengo un fragmento, que toma la URL y llega a esa URL y da el resultado:

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

Intenté envolverlo dentro de una función y devolver un valor como este:

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

Porque en mi código Node.js, tengo muchas if-elsedeclaraciones donde urlToCallse decidirá el valor de , como esta:

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

La cuestión es que todas las declaraciones dentro de a urllib.requestseguirán siendo las mismas, excepto el valor de urlToCall. Así que definitivamente necesito poner ese código común dentro de una función. Intenté lo mismo pero en doCallsiempre me volverá undefined. Intenté así:

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

Pero si imprimo valor en doCall()su interior, se imprime perfectamente, pero siempre volverá undefined. Según mi investigación, supe que no podemos devolver valores de funciones de devolución de llamada. (es verdad)? Si es así, ¿alguien puede aconsejarme cómo manejar esta situación, ya que quiero evitar el código duplicado en todos los if-elsebloques?

6
  • "¿es verdad?" - sí definitivamente. 28/04/2014 a las 11:32
  • @JanDvorak, ¿entonces no tengo otra opción que no sea duplicar el código? ;) 28/04/14 a las 11:33
  • 1
    ¿Sería útil pasar algunas de sus propias devoluciones de llamada? Eso creo. 28/04/14 a las 11:36
  • 6
    Creo que este enlace lo ayudaría a comprender cómo funciona: github.com/maxogden/art-of-node#callbacks 28/04/14 a las 11:41
  • 1
    @RodrigoMedeiros gracias por excelente recurso. Lo estoy pasando, me ayudó mucho :) 28/04/14 a las 11:54
142

Es undefinedporque, console.log(response)corre antes de doCall(urlToCall);que termine. También debe pasar una función de devolución de llamada, que se ejecuta cuando se realiza su solicitud .

Primero, tu función. Pásale una devolución de llamada:

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);
    });
}

Ahora:

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

@Rodrigo, publicó un buen recurso en los comentarios. Lea acerca de las devoluciones de llamada en el nodo y cómo funcionan. Recuerde, es un código asincrónico.

6
  • 21
    Esto funciona bien. Pero tengo una pregunta. al llamar a la función doCall dentro de eso. ¿Cómo puedo devolver la variable de respuesta? ¿Si, por ejemplo, quiero obtener esa variable fuera de la función?
    Romeo
    19 de julio de 2016 a las 1:37
  • 6
    @Romeo la misma pregunta aquí !! 5/04/2017 a las 9:51
  • esta publicación fue muy útil, ¡gracias! 7 de septiembre de 2018 a las 21:20
  • 2
    Hola, lo siento si estoy entendiendo mal, pero ¿en qué se diferencia esto? La respuesta de urllib ahora está atascada dentro del alcance de otra función fuera de ella. ¿No es como si ahora pudiera usar el objeto de respuesta en cualquier lugar fuera de doCall?
    alexr89
    22 jul.20 a las 15:00
  • Muy útil. Siempre evité crear devoluciones de llamada, pero ahora lo entiendo. Gracias 1 sep.2020 a las 17:43
20

I am facing small trouble in returning a value from callback function in Node.js

Esto no es un "pequeño problema", en realidad es imposible "devolver" un valor en el sentido tradicional de una función asincrónica.

Como no puede "devolver el valor", debe llamar a la función que necesitará el valor una vez que lo tenga. @display_name ya respondió a su pregunta, pero solo quería señalar que la devolución en doCall no devuelve el valor de la manera tradicional. Puede escribir doCall de la siguiente manera:

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 línea callback(finalData);es lo que llama a la función que necesita el valor que obtuvo de la función asíncrona. Pero tenga en cuenta que la declaración de retorno se usa para indicar que la función termina aquí , pero no significa que el valor se devuelve a la persona que llama (la persona que llama ya ha avanzado).

5

Código de ejemplo para node.js - función asíncrona a función de sincronización:

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);
}
1
4

Si lo que quieres es que tu código funcione sin modificar demasiado. Puede probar esta solución que elimina las devoluciones de llamada y mantiene el mismo flujo de trabajo de código :

Dado que está usando Node.js, puede usar co y co-request para lograr el mismo objetivo sin preocupaciones de devolución de llamada.

Básicamente, puedes hacer algo como esto:

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;
  });
}

Luego,

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

Al hacer esto, esperamos hasta que finalice la función de devolución de llamada, luego obtenemos el valor de ella. De alguna manera, resuelve tu problema.

4
  • pero esto es ecma6 soultion 18 de mayo de 2016 a las 7:21
  • y también impacta mi desempeño 18 de mayo de 2016 a las 7:22
  • ¿Es ecma6 un problema? 18 de mayo de 2016 a las 7:28
  • No, eso no es un problema, pero el problema es que muchos de nosotros todavía no usamos ECMA6 en su proyecto, ya que node.js tiene algún problema con la compatibilidad con ECMA. 18 de mayo de 2016 a las 8:02