En el nodo, muchos objetos emiten eventos. Por ejemplo, un servidor TCP transmitirá un evento de "conexión" cada vez que un cliente solicite una conexión y, por ejemplo, cada vez que se lea una pieza completa de datos, el sistema de archivos transmitirá un evento de "datos". Estos objetos se llaman emisores de eventos en el nodo. El transmisor de eventos permite a los programadores suscribirse a eventos de interés y vincular la función de devolución de llamada a los eventos relacionados, de modo que la función de devolución de llamada se llame cada vez que el transmisor de eventos emite un evento. El modo de publicación/suscripción es muy similar al modo GUI tradicional, como el programa recibirá notificaciones correspondientes cuando se haga clic en el botón. Usando este modelo, los programas del servidor pueden reaccionar cuando ocurren algunos eventos, como la conexión del cliente, los datos disponibles en el socket o el archivo cerrado.
También puede crear su propio transmisor de eventos. De hecho, Node proporciona una pseudo-clase especial de EventEmitter que puede usarse como una clase base para crear su propio transmisor de eventos.
Comprender el modo de devolución de llamada
La programación asíncrona no utiliza valores de retorno de funciones para indicar el final de una llamada de función, pero adopta un estilo de pase posterior.
El "estilo de avance de continuación" (CPS: estilo de avance de continuación) es un estilo de programación, y el control de procesos se pasa explícitamente a la siguiente operación ...
Las funciones de estilo CPS aceptarán una función como un parámetro adicional. Esta función se utiliza para señalar explícitamente el siguiente proceso de control del programa. Cuando la función CPS calcula su "valor de retorno", llamará a la función que representa el siguiente proceso del programa y tomará el "valor de retorno" de la función CPS como su parámetro.
De Wikipedia - http://en.wikipedia.org/wiki/continuation-passing_style
En este estilo de programación, cada función llamará a una función de devolución de llamada después de la ejecución, para que el programa pueda continuar ejecutándose. Más tarde comprenderá que JavaScript es muy adecuado para este estilo de programación. Aquí hay un ejemplo de cargar archivos en memoria en el nodo:
La copia del código es la siguiente:
var fs = require ('fs');
fs.ReadFile ('/etc/passwd', function (err, fileContent) {
if (err) {
tirar err;
}
console.log ('File Content', fileContent.ToString ());
});
En este ejemplo, pasa una función anónima en línea como el segundo parámetro de Fs.ReadFile. De hecho, esto está utilizando la programación CPS porque entrega el proceso posterior de la ejecución del programa a la función de devolución de llamada.
Como puede ver, el primer parámetro de la función de devolución de llamada es un objeto de error. Si se produce un error en el programa, este parámetro será una instancia de la clase de error. Este es un patrón común para la programación de CPS en el nodo.
Comprender el modo transmisor de eventos
En el modo de devolución de llamada estándar, se pasa una función como un parámetro para la función que se ejecutará. Este modo funciona muy bien en escenarios en los que se debe notificar al cliente después de completar la función. Sin embargo, si ocurren múltiples eventos durante la ejecución de una función o eventos ocurren varias veces, este patrón no es adecuado. Por ejemplo, si desea recibir una notificación cada vez que el socket reciba datos disponibles, encontrará que el modo de devolución de llamada estándar no es muy útil en este escenario. En este momento, el modo transmisor de eventos es útil. Puede usar un conjunto de interfaces estándar para separar claramente el generador de eventos y el oyente de eventos.
Al usar el modo Generador de eventos, están involucrados dos o más objetos: transmisores de eventos y uno o más oyentes de eventos.
Un transmisor de eventos, como su nombre indica, es un objeto que puede generar eventos. El oyente del evento está vinculado al transmisor de eventos para escuchar tipos específicos de eventos, como el siguiente ejemplo:
La copia del código es la siguiente:
var req = http.request (opciones, función (respuesta) {
Response.on ("Data", function (Data) {
console.log ("Algunos datos de la respuesta", datos);
});
respuesta.on ("end", function () {
console.log ("Respuesta finalizada");
});
});
req.end ();
Este código demuestra dos pasos necesarios al crear una solicitud HTTP para acceder a un servidor HTTP remoto utilizando la API HTTP.Request de Node (consulte el capítulo posterior). La primera línea adopta el "estilo de pase de continuación" (CPS: estilo de continuación), que pasa una función en línea que se llamará cuando HTTP responde. La API de solicitud HTTP usa CPS aquí porque el programa debe continuar realizando operaciones posteriores después de ejecutar la función HTTP.Request.
Cuando se ejecuta http.request, se llamará a la función anónima de devolución de llamada, y el objeto de respuesta HTTP se le pasa como un parámetro. Este objeto de respuesta HTTP es un transmisor de eventos. Según el documento de nodo, puede emitir muchos eventos, incluidos datos y fin. Las funciones de devolución de llamada que se registren se llamará cada vez que ocurra el evento.
Como lección, use el modo CPS cuando necesite recuperar los derechos de ejecución después de completar la operación solicitada, y use el modo transmisor de eventos cuando los eventos puedan ocurrir varias veces.
Comprender los tipos de eventos
Los eventos transmitidos tienen un tipo representado por una cadena. El ejemplo anterior contiene dos tipos de eventos: "Datos" y "Fin", que son cualquier cadena definida por el transmisor de eventos. Sin embargo, es convencionalmente constitutivo que los tipos de eventos generalmente se componen de palabras minúsculas que no contienen caracteres vacíos.
No puede usar el código para inferir qué tipos de eventos puede generar el transmisor de eventos, porque la API del transmisor de eventos no tiene un mecanismo de introspección, por lo que la API que está utilizando debe tener documentación para indicar que puede emitir ese tipo de eventos.
Una vez que ocurre un evento, el transmisor del evento llamará al oyente relacionado con el evento y pasará los datos relevantes al oyente como parámetro. En el ejemplo anterior http.request, la función de devolución de llamada de eventos "Datos" acepta un objeto de datos como su primer y único parámetro, mientras que "End" no acepta ningún dato. Estos parámetros como parte del contrato de API también están definidos subjetivamente por el autor de la API. Las firmas de parámetros de estas funciones de devolución de llamada también se explicarán en la documentación de la API de cada emisor de eventos.
Aunque el transmisor de eventos es una interfaz que sirve a todo tipo de eventos, el evento "Error" es una implementación especial en el nodo. La mayoría de los transmisores de eventos en el nodo generarán un evento de "error" cuando ocurra un error en el programa. Si el programa no escucha el evento de "error" de un transmisor de eventos, el transmisor de eventos notará y lanzará una excepción no captura hacia arriba cuando ocurra el error.
Puede ejecutar el siguiente código en el nodo perl para probar el efecto, que simula un transmisor de eventos que puede generar dos eventos:
La copia del código es la siguiente:
var em = new (requirir ('eventos'). EventEmitter) ();
em.emit ('Event1');
em.emit ('error', nuevo error ('mi error'));
Verá la siguiente salida:
La copia del código es la siguiente:
var em = new (requirir ('eventos'). EventEmitter) ();
indefinido
> em.emit ('Event1');
FALSO
> em.emit ('error', nuevo error ('mi error'));
Error: mi error
en replicate: 1: 18
en replserver.eval (repl.js: 80: 21)
en repl.js: 190: 20
en replserver.eval (repl.js: 87: 5)
en la interfaz. <Nónimo> (repl.js: 182: 12)
en interface.emit (event.js: 67: 17)
en interface._online (readline.js: 162: 10)
en interface._line (readline.js: 426: 8)
en interface._ttywrite (readline.js: 603: 14)
en Readstream. <Nónimo> (Readline.js: 82: 12)
>
En la línea 2 del código, se emite un evento llamado "Event1", sin ningún efecto, pero cuando se emite el evento "Error", el error se lanza a la pila. Si el programa no se está ejecutando en el entorno de la línea de comandos Perl, el programa se bloqueará debido a una excepción no captura.
Usando la API del transmisor de eventos
Cualquier objeto que implementa el modo transmisor de eventos (como el socket TCP, la solicitud HTTP, etc.) implementa el siguiente conjunto de métodos:
La copia del código es la siguiente:
.addlistener y .on - Agregar oyente de eventos para eventos del tipo especificado
.ONCE - Bida a un oyente de eventos que se ejecuta solo una vez para el evento del tipo especificado
.removeEventListener - Elimine un oyente atado al evento especificado
.removealleventListener - Elimine todos los oyentes atados al evento especificado
Presentemos en detalle a continuación.
Funciones vinculantes de devolución de llamada usando .addListener () o .on ()
Al especificar el tipo de evento y la función de devolución de llamada, puede registrar las acciones realizadas cuando ocurre el evento. Por ejemplo, si hay bloques de datos disponibles cuando un archivo lee un flujo de datos, emitirá un evento de "datos". El siguiente código muestra cómo pasar una función de devolución de llamada para que el programa le indique que ha ocurrido el evento de datos.
La copia del código es la siguiente:
función recibida (datos) {
console.log ("Obtiene datos de archivo de lectura de archivo: %j", datos);
}
readStream.AddListener ("Datos", recibido);
También puede usar .on, que es solo la abreviatura de .addListener. El siguiente código es el mismo que el anterior:
La copia del código es la siguiente:
función recibida (datos) {
console.log ("Obtiene datos de archivo de lectura de archivo: %j", datos);
}
readstream.on ("datos", recibeata);
En el código anterior, puede usar una función con nombre definida de antemano como la función de devolución de llamada, y también puede usar una función anónima en línea para simplificar el código:
La copia del código es la siguiente:
readstream.on ("data", function (data) {
console.log ("Obtiene datos de archivo de lectura de archivo: %j", datos);
});
Como se mencionó anteriormente, el número de parámetros y firmas pasadas a la función de devolución de llamada depende del objeto del transmisor de eventos específicos y el tipo de evento. No están estandarizados. El evento "Data" puede pasar un objeto de búfer de datos, el evento "Error" pasa un objeto de error y el evento "FIN" del flujo de datos no pasa ningún dato al oyente del evento.
Atar a múltiples oyentes de eventos
El modo de transmisor de eventos permite que múltiples oyentes de eventos escuchen el mismo tipo de evento del mismo transmisor de eventos, como:
La copia del código es la siguiente:
Tengo algunos datos aquí.
Yo también tengo algunos datos aquí.
El transmisor de eventos es responsable de invocar a todos los oyentes atados en el tipo de evento especificado en el orden en que está registrado el oyente, es decir:
1. Cuando ocurre un evento, el oyente del evento no puede llamarse de inmediato, y puede haber otros oyentes de eventos llamados antes.
2. Es anormal ser arrojado a la pila, lo que puede deberse a un error en el código. Cuando se transmite un evento, si un oyente de eventos lanza una excepción cuando se llama, es posible que algunos oyentes de eventos nunca se llamen. En este caso, el transmisor del evento capta la excepción y también puede manejarla.
Vea el siguiente ejemplo:
La copia del código es la siguiente:
readstream.on ("data", function (data) {
arrojar un nuevo error ("Algo mal ha sucedido");
});
readstream.on ("data", function (data) {
console.log ('Yo también tengo algunos datos aquí');
});
Debido a que el primer oyente lanza una excepción, no se llamará al segundo oyente.
Use .RemoVelistener () para eliminar un oyente de eventos del transmisor de eventos
Si ya no le importa un evento en un objeto, puede cancelar el oyente de eventos registrado especificando el tipo de evento y la función de devolución de llamada, así:
La copia del código es la siguiente:
función recibida (datos) {
console.log ("Obtiene datos de archivo de lectura de archivo: %j", datos);
}
readstream.on ("datos", recibeata);
// ...
readStream.RemoVelistener ("Datos", RecibeATA);
En este ejemplo, la última línea elimina a un oyente de eventos que puede llamarse en cualquier momento en el futuro del objeto del transmisor de eventos.
Para eliminar el oyente, debe nombrar la función de devolución de llamada, porque se requiere el nombre de la función de devolución de llamada al agregarla y eliminarla.
Use .once () para habilitar la función de devolución de llamada se ejecutará como máximo una vez
Si desea escuchar un evento que se ejecuta como máximo una vez, o solo está interesado en la primera vez que ocurre un evento, puede usar la función .once ():
La copia del código es la siguiente:
función recibida (datos) {
console.log ("Obtiene datos de archivo de lectura de archivo: %j", datos);
}
readStream.once ("Data", RecibeATA);
En el código anterior, la función recibida solo se llamará una vez. Si el objeto ReadStream emite un evento de datos, la función de devolución de llamada RETIVEATA se activará y solo se activará una vez.
En realidad, es solo un método conveniente, porque se puede implementar de manera muy simple, así:
La copia del código es la siguiente:
var eventEmitter = request ("Events"). EventEmitter;
EventEmitter.prototype.once = function (type, llamado) {
var que = esto;
this.on (type, function listyer () {
que.removelistener (tipo, oyente);
callback.apply (que, argumentos);
});
};
En el código anterior, redefine la función EventEmitter.prototype.once, y también redefinir la función una vez que cada objeto heredado de Eventemitter. El código simplemente usa el método .on (). Una vez que se recibe un evento, use .RemoveEventListener () para cancelar el registro de la función de devolución de llamada y llamar a la función de devolución de llamada original.
NOTA: El código anterior usa el método function.apply (), que acepta un objeto y lo toma como la variable contenida y una matriz de parámetros. En el ejemplo anterior, la matriz de parámetros no modificada se pasa transparentemente a la función de devolución de llamada a través del transmisor de eventos.
Elimine todos los oyentes de eventos del transmisor de eventos con .removealllisteners ()
Puede eliminar todos los oyentes registrados al tipo de evento especificado del transmisor de eventos de la siguiente manera:
La copia del código es la siguiente:
emitter.removealllisteners (tipo);
Por ejemplo, puede cancelar el oyente de todas las señales de interrupción del proceso como esta:
La copia del código es la siguiente:
process.removealllisteners ("Sigterter");
Nota: Como lección, se recomienda que solo use esta función cuando sepa exactamente lo que se elimina. De lo contrario, debe dejar que otras partes de la aplicación eliminen la colección de oyentes de eventos, o también puede dejar que esas partes del programa se hagan cargo de eliminar al oyente. Pero pase lo que pase, esta función sigue siendo útil en algunos escenarios raros, como cuando se está preparando para cerrar un transmisor de eventos o cerrar todo el proceso de manera ordenada.
Crear un transmisor de eventos
Los transmisores de eventos hacen que las interfaces de programación sean más generales de una manera excelente. En un modo de programación común y fácil de entender, el cliente llama directamente a varias funciones, mientras que en el modo transmisor de eventos, el cliente está vinculado a varios eventos, lo que hará que su programa sea más flexible. (Nota del traductor: esta oración no es muy segura, publicando el texto original: el emisor de eventos proporciona una excelente manera de hacer que una interfaz de programación sea más genérica. Cuando usa un patrón de comprensión común, los clientes se unen a los eventos en lugar de invocar funciones, haciendo que su programa sea más flexible).
Además, al usar transmisores de eventos, también puede obtener muchas características, como vincular múltiples oyentes no relacionados en el mismo evento.
Heredado del transmisor de eventos de nodo
Si está interesado en el modo de emisor de eventos de Node y tiene la intención de usarlo en su propia aplicación, puede crear una pseudo-clase heredando EventEmitter:
La copia del código es la siguiente:
util = require ('util');
var eventEmitter = request ('events'). EventEmitter;
// Este es el constructor de MyClass:
var myclass = function () {
}
Util.inherits (MyClass, EventEmitter);
Nota: Util.inherits crea la cadena prototipo de MyClass, para que su instancia de MyClass pueda usar el método prototipo de EventEmitter.
Evento de lanzamiento
Al heredar de Eventemitter, MyClass puede lanzar eventos como este:
La copia del código es la siguiente:
Myclass.prototype.somemethod = function () {
this.emit ("Evento personalizado", "Argumento 1", "Argumento 2");
};
En el código anterior, cuando la instancia de MyClass llame al método SomEMethond, se emitirá un evento llamado "Evento de CuteOM". Este evento también emitirá dos cadenas como datos: "Argumento 1" y "Argumento 2", que se pasará como parámetros para el oyente del evento.
El cliente de la instancia de MyClass puede escuchar el evento "Evento personalizado" como este:
La copia del código es la siguiente:
var myInstance = new myClass ();
myInstance.on ('Evento personalizado', function (str1, str2) {
console.log ('obtuve un evento personalizado con el str1 %sy str2 %s!', str1, str2);
});
Por ejemplo, puede crear una clase de ticker que emite eventos "Marque" una vez por segundo:
La copia del código es la siguiente:
var util = require ('util'),
EventEmitter = requirir ('eventos'). EventEmitter;
var ticker = function () {
var self = this;
setInterval (function () {
self.emit ('tick');
}, 1000);
};
Util.inherits (Ticker, EventEmitter);
Los clientes que usan la clase Ticker pueden mostrar cómo usar la clase Ticker y escuchar eventos "Tick".
La copia del código es la siguiente:
var ticker = new Ticker ();
ticker.on ("tick", function () {
console.log ("tick");
});
resumen
El modo transmisor de eventos es un patrón de reingreso que se puede usar para desacoplar el objeto del transmisor de eventos desde un conjunto de código para un evento específico.
Puede usar event_emitter.on () para registrar a los oyentes para tipos específicos de eventos y no registrarse con event_emitter.removelistener ().
También puede crear su propio emisor de eventos heredando EventEmitter y simplemente usando la función .Emit ().