Cómo escribir funciones asincrónicas para Node.js

114

Intenté investigar cómo se deben escribir exactamente las funciones asincrónicas. Después de revisar mucha documentación, todavía no me queda claro.

¿Cómo escribo funciones asincrónicas para Node? ¿Cómo debo implementar correctamente el manejo de eventos de error?

Otra forma de hacer mi pregunta sería la siguiente: ¿Cómo debo interpretar la siguiente función?

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Además, encontré esta pregunta en SO ("¿Cómo creo una función asincrónica sin bloqueo en node.js?") Interesante. No siento que haya sido respondida todavía.

Kriem
fuente
14
Por eso estoy preguntando. No me resulta evidente en qué se diferencian estas funciones.
Kriem
Te recomiendo que mires setTimeouty setIntervalen tu navegador favorito y juegues con ellos también. O devoluciones de llamada ajax (probablemente lo más parecido a la experiencia del nodo), o detectores de eventos para cosas con las que está familiarizado, como hacer clic y cargar eventos. El modelo asincrónico ya existe en el navegador y son exactamente iguales en el nodo.
davin
@davin - Supongo que entonces no comprendo completamente el modelo asincrónico.
Kriem
@Kriem, respondí algo ayer que podría ayudar: stackoverflow.com/questions/6883648/… No es una respuesta a tu pregunta, pero está relacionada con el tema. Intente leer la pregunta y la respuesta allí y juegue con el código para tratar de comprender lo que está sucediendo.
davin
2
@Raynos ¿Cuál es la definición de "función asíncrona"?
Anderson Green

Respuestas:

85

Parece que confunde la E / S asíncrona con las funciones asíncronas. node.js usa E / S sin bloqueo asincrónico porque es mejor el E / S sin bloqueo. La mejor manera de entenderlo es ir a ver algunos videos de ryan dahl.

¿Cómo escribo funciones asincrónicas para Node?

Simplemente escriba funciones normales, la única diferencia es que no se ejecutan de inmediato, sino que se transmiten como devoluciones de llamada.

¿Cómo debo implementar el manejo de eventos de error correctamente?

Generalmente, las API le devuelven la llamada con un error como primer argumento. Por ejemplo

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

Es un patrón común.

Otro patrón común es on('error'). Por ejemplo

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

Editar:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

La función anterior cuando se llama como

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

Imprimirá 42en la consola de forma asincrónica. En particular, se process.nextTickactiva después de que la pila de llamadas de eventloop actual esté vacía. Esa pila de llamadas está vacía después async_functiony se console.log(43)ha ejecutado. Entonces imprimimos 43 seguido de 42.

Probablemente debería leer un poco sobre el ciclo de eventos.

Raynos
fuente
He visto los videos de Dahl, pero me temo que no entiendo el tema. :(
Kriem
1
@Kriem vea la respuesta actualizada y lea sobre el ciclo del evento
Raynos
1
Gracias por las ideas. Ahora soy más consciente de lo que me falta en conocimientos. :) Tu último ejemplo ayudó por cierto.
Kriem
Creo que su declaración sobre IO asincrónica es "mejor" es demasiado general. En este sentido sí, pero en general puede que no sea el caso.
Jake B
En su primer ejemplo de código, verifica el argumento err, pero no regresó después. En el caso de un error, el código continuará y potencialmente causará serios problemas en su aplicación.
Gabriel McAdams
9

Simplemente pasar por las devoluciones de llamada no es suficiente. Tienes que usar settimer, por ejemplo, para que la función sea asincrónica.

Ejemplos: funciones no asincrónicas:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

Si va a ejecutar el ejemplo anterior, esto debería ser bueno, tendrá que esperar hasta que esas funciones terminen de funcionar.

Funciones de pseudo multiproceso (asíncrono):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

Este será realmente asincrónico. Esto debería estar bien escrito antes de que finalice el async.

calmado
fuente
3

Si SABE que una función devuelve una promesa, le sugiero que utilice las nuevas funciones async / await en JavaScript. Hace que la sintaxis parezca sincrónica pero que funcione de forma asincrónica. Cuando agrega la asyncpalabra clave a una función, le permite hacer awaitpromesas en ese ámbito:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

si una función no devuelve una promesa, recomiendo envolverla en una nueva promesa que defina, luego resuelva los datos que desee:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

En pocas palabras: aproveche el poder de las promesas.

ryanwaite28
fuente
Lo que hay que recordar aquí es que el cuerpo de la promesa todavía se ejecuta sincrónicamente.
shadow0359
2

Prueba esto, funciona tanto para el nodo como para el navegador.

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});
Pradeep
fuente
18
4 votos negativos y ni siquiera un comentario constructivo ..: \
Omer
6
@Omer Así es la vida en SO.
Piece Digital
6
@NorbertoBezi Quizás el código se explica por sí mismo para usted, pero no para el que publicó la respuesta. Es por eso que siempre es una buena práctica explicar las votaciones negativas.
Omer
0

He negociado demasiadas horas para tal tarea en node.js. Soy principalmente un chico de front-end.

Encuentro esto bastante importante, porque todos los métodos de nodo asíncronos tratan con la devolución de llamada, y transformarlo en Promise es mejor manejarlo.

Solo quiero mostrar un resultado posible, más sencillo y legible. Usando ECMA-6 con async puedes escribirlo así.

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

el (undefined || null)es para escenarios de respuesta (bucle de impresión de evento de lectura), el uso de undefined también funciona.

Yoarthur
fuente