2012-03-30 15 views
9

Tengo una aplicación nodo que es no una aplicación web - completa una serie de tareas asíncronas antes de regresar 1. Inmediatamente antes de regresar, los resultados del programa se imprimen en la consola.Cómo escribir una función node.js que espera a que un evento se active antes de 'regresar'?

¿Cómo me aseguro de que todo el trabajo asíncrono se complete antes de volver? Pude lograr algo similar a esto en una aplicación web asegurándome de todas las tareas que completé antes de llamar a res.end(), pero no tengo ningún equivalente para un 'evento' final al que llamar antes de permitir el retorno de un script.

Véase más abajo para mi función (roto) Actualmente, tratando de esperar hasta que la pila de llamadas está vacía. Acabo de descubrir que este es un tipo de enfoque sin sentido porque el nodo espera que processHub se complete antes de ingresar a cualquiera de las funciones asincrónicas llamadas en processObjWithRef.

function processHub(hubFileContents){ 
    var callStack = []; 
    var myNewObj = {}; 
    processObjWithRef(samplePayload, myNewObj, callStack); 
    while(callStack.length>0){ 
     //do nothing 
    } 
    return 1 
} 

Nota: He intentado muchas veces anteriormente para lograr este tipo de comportamiento con las bibliotecas como asíncrono (véase mi pregunta relacionada a How can I make this call to request in nodejs synchronous?) así que por favor tome la respuesta y los comentarios allí en cuenta antes de sugerir ninguna respuesta basada en la " solo usa asynch '.

+1

"¿Cómo me aseguro de que todo el trabajo asíncrono se completa antes de volver ? " Es una pregunta sin sentido: nunca se devuelve el control al shell hasta que se hayan completado todas las tareas asíncronas, por lo que la salida de la consola hasta después de que la última función asíncrona haya terminado solucionó mi problema. Gracias a todos por enviar respuestas. – Trindaz

Respuesta

5

El problema está en el diseño de la función. Desea devolver un resultado sincrónico de una lista de tareas que se ejecutan de forma asincrónica.

Debe implementar su función con un parámetro adicional que será la devolución de llamada donde se pondría el resultado (en este caso, 1) para algunos consumidores a hacer algo con él.

También es necesario tener un parámetro de devolución de llamada en su función interna, de lo contrario no se sabe cuando termina. Si esto último no es posible, entonces debe hacer algún tipo de sondeo (usando setInterval tal vez) para probar cuando se llena la matriz callStack.

Recuerde, en Javascript nunca debe hacer una espera ocupada. Eso bloqueará completamente tu programa ya que se ejecuta en un solo proceso.

1

El problema es que node.js es de un solo subproceso, lo que significa que si una función se ejecuta, nada carreras demás (evento-loop) hasta que ha vuelto esa función. Por lo tanto, no puede bloquear una función para que vuelva luego de que se completen las tareas de sincronización.

Podría, por ejemplo, configurar una variable de contador que cuente las tareas comenzadas como asincrónicas y disminuir ese contador usando una función de devolución de llamada (que se llama después de que la tarea haya terminado) desde su código asíncrono.

5

No se puede esperar a que un evento asíncrono antes de regresar - que es la definición de asíncrona! Tratar de forzar a Node a este estilo de programación solo te causará dolor. Un ejemplo ingenuo sería comprobar periódicamente si el callstack está vacío.

var callstack = [...]; 

function processHub(contents) { 
    doSomethingAsync(..., callstack); 
} 

// check every second to see if callstack is empty 
var interval = setInterval(function() { 
    if (callstack.length == 0) { 
    clearInterval(interval); 
    doSomething() 
    } 
}, 1000); 

En cambio, la forma habitual de hacer las cosas asíncrono en el Nodo es implementar una devolución de llamada a la función.

function processHub(hubFileContents, callback){ 
    var callStack = []; 
    var myNewObj = {}; 
    processObjWithRef(samplePayload, myNewObj, callStack, function() { 
    if (callStack.length == 0) { 
     callback(some_results); 
    } 
    }); 
} 

Si realmente quiere devolver algo, echa un vistazo a promises; se garantiza que emitirán un evento de manera inmediata o en algún momento en el futuro cuando se resuelvan.

function processHub(hubFileContents){ 
    var callStack = []; 
    var myNewObj = {}; 
    var promise = new Promise(); 

    // assuming processObjWithRef takes a callback 
    processObjWithRef(samplePayload, myNewObj, callStack, function() { 
    if (callStack.length == 0) { 
     promise.resolve(some_results); 
    } 
    }); 

    return promise; 
} 

processHubPromise = processHub(...); 
processHubPromise.then(function(result) { 
    // do something with 'result' when complete 
}); 
0

Tendrá que empezar a diseñar y pensar de forma asíncrona, que puede tomar un poco de tiempo para acostumbrarse al principio. Este es un ejemplo simple de cómo abordaría algo como "regresar" después de una llamada a función.

function doStuff(param, cb) { 
    //do something 
    var newData = param; 
    //"return" 
    cb(newData); 
} 

doStuff({some:data}, function(myNewData) { 
    //you're done with doStuff in here 
}); 

También hay una gran cantidad de funciones de utilidad votos en la biblioteca async disponibles en la NGP.

3

deasync se diseñó para abordar su problema exactamente. Basta con sustituir

while(callStack.length>0){ 
    //do nothing 
} 

con

require('deasync').loopWhile(function(){return callStack.length>0;}); 
1

Node.js se ejecuta en un bucle de eventos de un solo subproceso y aprovecha las llamadas asíncronas para hacer varias cosas, como las operaciones de E/S.

si tiene que esperar a que una serie de operaciones asincrónicas para terminar antes de ejecutar código adicional puede probar a usar asíncrono -

Node.js Async Tutorial

Cuestiones relacionadas