Emitir eventos
Hay varias formas de enviar eventos entre el servidor y el cliente.
Para usuarios de TypeScript, es posible proporcionar sugerencias de tipo para los eventos. Por favor revisa esto.
Emit básico
La API de Socket.IO está inspirada en el EventEmitter de Node.js, lo que significa que puedes emitir eventos de un lado y registrar listeners del otro:
Servidor
io.on("connection", (socket) => {
socket.emit("hello", "world");
});
Cliente
socket.on("hello", (arg) => {
console.log(arg); // world
});
Esto también funciona en la otra dirección:
Servidor
io.on("connection", (socket) => {
socket.on("hello", (arg) => {
console.log(arg); // world
});
});
Cliente
socket.emit("hello", "world");
Puedes enviar cualquier número de argumentos, y todas las estructuras de datos serializables son soportadas, incluyendo objetos binarios como Buffer o TypedArray.
Servidor
io.on("connection", (socket) => {
socket.emit("hello", 1, "2", { 3: '4', 5: Buffer.from([6]) });
});
Cliente
// lado del cliente
socket.on("hello", (arg1, arg2, arg3) => {
console.log(arg1); // 1
console.log(arg2); // "2"
console.log(arg3); // { 3: '4', 5: ArrayBuffer (1) [ 6 ] }
});
No hay necesidad de ejecutar JSON.stringify() en objetos ya que se hará por ti.
// MAL
socket.emit("hello", JSON.stringify({ name: "John" }));
// BIEN
socket.emit("hello", { name: "John" });
Notas:
Los objetos Date serán convertidos a (y recibidos como) su representación en string, ej.
1970-01-01T00:00:00.000Z
const serializedMap = [...myMap.entries()];
const serializedSet = [...mySet.keys()];
- puedes usar el método
toJSON()para personalizar la serialización de un objeto
Ejemplo con una clase:
class Hero {
#hp;
constructor() {
this.#hp = 42;
}
toJSON() {
return { hp: this.#hp };
}
}
socket.emit("here's a hero", new Hero());
Acknowledgements
Los eventos son geniales, pero en algunos casos podrías querer una API más clásica de solicitud-respuesta. En Socket.IO, esta característica se llama acknowledgements.
Puedes agregar un callback como último argumento del emit(), y este callback será llamado una vez que el otro lado confirme el evento:
Servidor
io.on("connection", (socket) => {
socket.on("update item", (arg1, arg2, callback) => {
console.log(arg1); // 1
console.log(arg2); // { name: "updated" }
callback({
status: "ok"
});
});
});
Cliente
socket.emit("update item", "1", { name: "updated" }, (response) => {
console.log(response.status); // ok
});
Con timeout
A partir de Socket.IO v4.4.0, ahora puedes asignar un timeout a cada emit:
socket.timeout(5000).emit("my-event", (err) => {
if (err) {
// el otro lado no confirmó el evento en el tiempo dado
}
});
También puedes usar tanto un timeout como un acknowledgement:
socket.timeout(5000).emit("my-event", (err, response) => {
if (err) {
// el otro lado no confirmó el evento en el tiempo dado
} else {
console.log(response);
}
});
Eventos volátiles
Los eventos volátiles son eventos que no serán enviados si la conexión subyacente no está lista (un poco como UDP, en términos de confiabilidad).
Esto puede ser interesante por ejemplo si necesitas enviar la posición de los personajes en un juego en línea (ya que solo los últimos valores son útiles).
socket.volatile.emit("hello", "podría o no ser recibido");
Otro caso de uso es descartar eventos cuando el cliente no está conectado (por defecto, los eventos se almacenan en buffer hasta la reconexión).
Ejemplo:
Servidor
io.on("connection", (socket) => {
console.log("connect");
socket.on("ping", (count) => {
console.log(count);
});
});
Cliente
let count = 0;
setInterval(() => {
socket.volatile.emit("ping", ++count);
}, 1000);
Si reinicias el servidor, verás en la consola:
connect
1
2
3
4
# el servidor se reinicia, el cliente se reconecta automáticamente
connect
9
10
11
Sin la bandera volatile, verías:
connect
1
2
3
4
# el servidor se reinicia, el cliente se reconecta automáticamente y envía sus eventos almacenados en buffer
connect
5
6
7
8
9
10
11