Quiero hacer una página que muestre algunos datos de una base de datos, por lo que he creado algunas funciones que obtienen esos datos de mi base de datos. Solo soy un novato en Node.js, por lo que entiendo, si quiero usarlos todos en una sola página (respuesta HTTP) tendría que anidarlos a todos:
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();
});
});
});
Si hay muchas funciones como esa, entonces el anidamiento se convierte en un problema .
Hay alguna manera de evitar esto? Supongo que tiene que ver con cómo combinas múltiples funciones asincrónicas, lo que parece ser algo fundamental.
javascript
asynchronous
functional-programming
node.js
Kay pálido
fuente
fuente
getSomeDate
ygetSomeOtherDate
termina cambiando la sangría de muchas líneas, lo que hace que el historial de git sea más difícil de leer (git blame
incluso es inútil después de esto), y es probable que cometas errores al hacerlo manualmenteRespuestas:
Interesante observación. Tenga en cuenta que en JavaScript normalmente puede reemplazar las funciones de devolución de llamada anónimas en línea con variables de función con nombre.
El seguimiento:
Podría reescribirse para parecerse a esto:
Sin embargo, a menos que planee reutilizar la lógica de devolución de llamada en otros lugares, a menudo es mucho más fácil leer funciones anónimas en línea, como en su ejemplo. También le ahorrará tener que encontrar un nombre para todas las devoluciones de llamada.
Además, tenga en cuenta que, como señaló @pst en un comentario a continuación, si está accediendo a las variables de cierre dentro de las funciones internas, lo anterior no sería una traducción directa. En tales casos, usar funciones anónimas en línea es aún más preferible.
fuente
getMoreData
se pierde el acceso a 'res' en .someDataParser
realidad analiza TODOS los datos, ya que también llamagetMoreData
. En ese sentido, el nombre de la función es incorrecto y se hace evidente que en realidad no hemos eliminado el problema de anidamiento.Kay, simplemente usa uno de estos módulos.
Se convertirá esto:
Dentro de esto:
fuente
En su mayor parte, estaría de acuerdo con Daniel Vassallo. Si puede dividir una función complicada y profundamente anidada en funciones con nombre separadas, entonces esa es generalmente una buena idea. Para los momentos en que tiene sentido hacerlo dentro de una sola función, puede usar una de las muchas bibliotecas asíncronas de node.js disponibles. Las personas han ideado muchas formas diferentes de abordar esto, así que eche un vistazo a la página de módulos de node.js y vea lo que piensa.
Yo mismo escribí un módulo para esto, llamado async.js . Con esto, el ejemplo anterior podría actualizarse a:
Una cosa buena de este enfoque es que puede cambiar rápidamente su código para obtener los datos en paralelo cambiando la función 'serie' a 'paralelo'. Además, async.js también funcionará dentro del navegador, por lo que puede usar los mismos métodos que usaría en node.js si encuentra algún código asincrónico complicado.
Espero que sea útil!
fuente
Podría usar este truco con una matriz en lugar de funciones anidadas o un módulo.
Mucho más fácil para los ojos.
Puede ampliar el idioma para procesos paralelos o incluso cadenas paralelas de procesos:
fuente
Me gusta mucho async.js para este propósito.
El problema se resuelve con el comando en cascada:
Ejemplo
En cuanto a las variables req, res, se compartirán dentro del mismo alcance que la función (req, res) {} que encierra toda la llamada async.waterfall.
No solo eso, async es muy limpio. Lo que quiero decir es que cambio muchos casos como este:
Primero:
Entonces a esto:
Entonces a esto:
También permite que muchas funciones prefabricadas preparadas para async se invoquen desde util.js muy rápido. Simplemente encadene lo que quiere hacer, asegúrese de que cb se maneje universalmente. Esto acelera mucho todo el proceso de codificación.
fuente
Lo que necesitas es un poco de azúcar sintáctica. Mira esto:
Bastante ordenado , ¿no? Puede notar que html se convirtió en una matriz. Esto se debe en parte a que las cadenas son inmutables, por lo que es mejor que proteja su salida en una matriz, que descartar cadenas cada vez más grandes. La otra razón es debido a otra buena sintaxis con
bind
.Queue
en el ejemplo es realmente solo un ejemplo y junto conpartial
se puede implementar de la siguiente manerafuente
last
función opcional )obj.email
y su próxima función usa yobj.email
luego la elimina (o simplemente asignanull
).Estoy enamorado de Async.js desde que lo encontré. Tiene una
async.series
función que puede usar para evitar anidamientos largos.Documentación:-
series (tareas, [devolución de llamada])
Ejecute una serie de funciones en serie, cada una ejecutándose una vez que se haya completado la función anterior. [...]
Argumentos
tasks
- Una serie de funciones para ejecutar, cada función recibe una devolución de llamada que debe llamar al finalizar.callback(err, [results])
- Una devolución de llamada opcional para ejecutarse una vez que se hayan completado todas las funciones. Esta función obtiene una matriz de todos los argumentos pasados a las devoluciones de llamada utilizadas en la matriz.Así es como podemos aplicarlo a su código de ejemplo: -
fuente
El azúcar sintáctico más simple que he visto es la promesa de nodo.
npm install node-promise || git clone https://github.com/kriszyp/node-promise
Con esto, puede encadenar métodos asíncronos como:
El valor de retorno de cada uno está disponible como argumento en el siguiente.
fuente
Lo que ha hecho allí es tomar un patrón asíncrono y aplicarlo a 3 funciones llamadas en secuencia, cada una esperando que se complete la anterior antes de comenzar, es decir, las ha sincronizado . El punto sobre la programación asíncrona es que puede tener varias funciones ejecutándose todas a la vez y no tener que esperar a que se complete cada una.
si getSomeDate () no proporciona nada para getSomeOtherDate (), que no proporciona nada para getMoreData (), ¿por qué no los llama de forma asincrónica como permite js o si son interdependientes (y no asincrónicos) escríbalos como función única?
No necesita usar el anidamiento para controlar el flujo; por ejemplo, haga que cada función termine llamando a una función común que determina cuándo se han completado los 3 y luego envía la respuesta.
fuente
Supongamos que pudieras hacer esto:
Solo necesita implementar chain () para que aplique parcialmente cada función a la siguiente, e inmediatamente invoca solo la primera función:
fuente
el infierno de devolución de llamada se puede evitar fácilmente en JavaScript puro con cierre. La solución a continuación supone que todas las devoluciones de llamada siguen la firma de la función (error, datos).
fuente
Recientemente he creado una abstracción más simple llamada wait.for para llamar a funciones asíncronas en modo de sincronización (basado en Fibras). Está en una etapa temprana pero funciona. Es en:
https://github.com/luciotato/waitfor
Usando wait.for , puede llamar a cualquier función asíncrona de nodejs estándar, como si fuera una función de sincronización.
usando wait.for su código podría ser:
... o si quieres ser menos detallado (y también agregar errores de captura)
En todos los casos, getSomeDate , getSomeOtherDate y getMoreData deben ser funciones asíncronas estándar con el último parámetro una devolución de llamada de función (err, data)
como en:
fuente
Para resolver este problema, escribí nodent ( https://npmjs.org/package/nodent ) que preprocesa invisiblemente su JS. Su código de ejemplo sería (asíncrono, realmente, lea los documentos).
Claramente, hay muchas otras soluciones, pero el preprocesamiento tiene la ventaja de tener poca o ninguna sobrecarga de tiempo de ejecución y, gracias al soporte del mapa de origen, también es fácil de depurar.
fuente
Yo tuve el mismo problema. He visto las principales bibliotecas para ejecutar funciones asíncronas de nodo, y presentan un encadenamiento no natural (debe usar tres o más métodos confs, etc.) para construir su código.
Pasé algunas semanas desarrollando una solución simple y fácil de leer. Por favor, intente con EnqJS . Todas las opiniones serán apreciadas.
En vez de:
con EnqJS:
Observe que el código parece ser más grande que antes. Pero no está anidado como antes. Para parecer más natural, las cadenas se llaman inmediatamente:
Y para decir que regresó, dentro de la función que llamamos:
fuente
Lo hago de una manera bastante primitiva pero efectiva. Por ejemplo, necesito obtener un modelo con sus padres e hijos y digamos que necesito hacer consultas separadas para ellos:
fuente
Use Fibras https://github.com/laverdet/node-fibers hace que el código asincrónico se vea como sincrónico (sin bloqueo)
Yo personalmente uso este pequeño contenedor http://alexeypetrushin.github.com/synchronize Muestra de código de mi proyecto (cada método es realmente asíncrono, trabajando con un archivo asincrónico IO) Incluso tengo miedo de imaginar qué desastre sería con la devolución de llamada o bibliotecas auxiliares de control de flujo asíncrono.
fuente
Task.js te ofrece esto:
En lugar de esto:
fuente
Después de que los demás respondieron, usted declaró que su problema eran variables locales. Parece que una manera fácil de hacer esto es escribir una función externa para contener esas variables locales, luego usar un grupo de funciones internas con nombre y acceder a ellas por nombre. De esta manera, solo anidará dos de profundidad, independientemente de cuántas funciones necesite encadenar.
Aquí está el intento de mi novato de usar el
mysql
módulo Node.js con anidamiento:La siguiente es una reescritura utilizando funciones internas con nombre. La función externa también
with_connection
se puede utilizar como soporte para variables locales. (Aquí, tengo los parámetrossql
,bindings
,cb
que actúan de una manera similar, pero sólo se puede definir algunas variables locales adicionales enwith_connection
.)Había estado pensando que quizás sería posible hacer un objeto con variables de instancia, y usar estas variables de instancia como reemplazo de las variables locales. Pero ahora encuentro que el enfoque anterior que usa funciones anidadas y variables locales es más simple y más fácil de entender. Se necesita un tiempo para desaprender OO, parece :-)
Así que aquí está mi versión anterior con un objeto y variables de instancia.
Resulta que
bind
se puede utilizar con alguna ventaja. Me permite deshacerme de las funciones anónimas algo feas que he creado que no hicieron mucho, excepto reenviarme a una llamada al método. No pude pasar el método directamente porque habría estado involucrado con el valor incorrecto dethis
. Pero conbind
, puedo especificar el valorthis
que quiero.Por supuesto, nada de esto es JS adecuado con la codificación Node.js, solo pasé un par de horas en ello. ¿Pero tal vez con un poco de pulido esta técnica puede ayudar?
fuente
async.js funciona bien para esto. Encontré este artículo muy útil que explica la necesidad y el uso de async.js con ejemplos: http://www.sebastianseilund.com/nodejs-async-in-practice
fuente
Si no desea utilizar "paso" o "seq", intente con "línea", que es una función simple para reducir la devolución de llamada asíncrona anidada.
https://github.com/kevin0571/node-line
fuente
C # como asyncawait es otra forma de hacer esto
https://github.com/yortus/asyncawait
fuente
Usando cable su código se vería así:
fuente
para su conocimiento considere Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase
fuente