Skip to main content
Version: 4.x

监听事件

有多种方法可以处理服务器和客户端之间传输的事件。

¥There are several ways to handle events that are transmitted between the server and the client.

事件触发器方法

¥EventEmitter methods

在服务器端,Socket 实例扩展了 Node.js EventEmitter 类。

¥On the server-side, the Socket instance extends the Node.js EventEmitter class.

在客户端,Socket 实例使用 component-emitter 库提供的事件触发器,该库公开了 EventEmitter 方法的子集。

¥On the client-side, the Socket instance uses the event emitter provided by the component-emitter library, which exposes a subset of the EventEmitter methods.

socket.on(eventName, listener)

将监听器函数添加到名为 eventName 的事件的监听器数组的末尾。

¥Adds the listener function to the end of the listeners array for the event named eventName.

socket.on("details", (...args) => {
// ...
});

socket.once(eventName, listener)

为名为 eventName 的事件添加一次性监听器函数

¥Adds a one-time listener function for the event named eventName

socket.once("details", (...args) => {
// ...
});

socket.off(eventName, listener)

从名为 eventName 的事件的监听器数组中删除指定的监听器。

¥Removes the specified listener from the listener array for the event named eventName.

const listener = (...args) => {
console.log(args);
}

socket.on("details", listener);

// and then later...
socket.off("details", listener);

socket.removeAllListeners([eventName])

删除所有监听器或指定 eventName 的监听器。

¥Removes all listeners, or those of the specified eventName.

// for a specific event
socket.removeAllListeners("details");
// for all events
socket.removeAllListeners();

捕获所有监听器

¥Catch-all listeners

从 Socket.IO v3 开始,受 EventEmitter2 库启发的新 API 允许声明包罗万象的监听器。

¥Since Socket.IO v3, a new API inspired from the EventEmitter2 library allows to declare catch-all listeners.

此功能在客户端和服务器上均可用。

¥This feature is available on both the client and the server.

socket.onAny(listener)

添加一个监听器,当触发任何事件时将触发该监听器。

¥Adds a listener that will be fired when any event is emitted.

socket.onAny((eventName, ...args) => {
// ...
});
caution

回执 没有被捕获在包罗万象的监听器中。

¥Acknowledgements are not caught in the catch-all listener.

socket.emit("foo", (value) => {
// ...
});

socket.onAnyOutgoing(() => {
// triggered when the event is sent
});

socket.onAny(() => {
// not triggered when the acknowledgement is received
});

socket.prependAny(listener)

添加一个监听器,当触发任何事件时将触发该监听器。监听器被添加到监听器数组的开头。

¥Adds a listener that will be fired when any event is emitted. The listener is added to the beginning of the listeners array.

socket.prependAny((eventName, ...args) => {
// ...
});

socket.offAny([listener])

删除所有捕获所有监听器或给定监听器。

¥Removes all catch-all listeners, or the given listener.

const listener = (eventName, ...args) => {
console.log(eventName, args);
}

socket.onAny(listener);

// and then later...
socket.offAny(listener);

// or all listeners
socket.offAny();

socket.onAnyOutgoing(listener)

为传出数据包注册一个新的捕获所有监听器。

¥Register a new catch-all listener for outgoing packets.

socket.onAnyOutgoing((event, ...args) => {
// ...
});
caution

回执 没有被捕获在包罗万象的监听器中。

¥Acknowledgements are not caught in the catch-all listener.

socket.on("foo", (value, callback) => {
callback("OK");
});

socket.onAny(() => {
// triggered when the event is received
});

socket.onAnyOutgoing(() => {
// not triggered when the acknowledgement is sent
});

socket.prependAnyOutgoing(listener)

为传出数据包注册一个新的捕获所有监听器。监听器被添加到监听器数组的开头。

¥Register a new catch-all listener for outgoing packets. The listener is added to the beginning of the listeners array.

socket.prependAnyOutgoing((event, ...args) => {
// ...
});

socket.offAnyOutgoing([listener])

删除先前注册的监听器。如果未提供监听器,则删除所有捕获所有监听器。

¥Removes the previously registered listener. If no listener is provided, all catch-all listeners are removed.

const listener = (eventName, ...args) => {
console.log(eventName, args);
}

socket.onAnyOutgoing(listener);

// remove a single listener
socket.offAnyOutgoing(listener);

// remove all listeners
socket.offAnyOutgoing();

验证

¥Validation

事件参数的验证超出了 Socket.IO 库的范围。

¥The validation of the event arguments is out of the scope of the Socket.IO library.

JS 生态系统中有许多包涵盖了这个用例,其中包括:

¥There are many packages in the JS ecosystem which cover this use case, among them:

joiacknowledgements 为例:

¥Example with joi and acknowledgements:

const Joi = require("joi");

const userSchema = Joi.object({
username: Joi.string().max(30).required(),
email: Joi.string().email().required()
});

io.on("connection", (socket) => {
socket.on("create user", (payload, callback) => {
if (typeof callback !== "function") {
// not an acknowledgement
return socket.disconnect();
}
const { error, value } = userSchema.validate(payload);
if (error) {
return callback({
status: "Bad Request",
error
});
}
// do something with the value, and then
callback({
status: "OK"
});
});

});

错误处理

¥Error handling

目前 Socket.IO 库中没有内置错误处理,这意味着你必须捕获监听器中可能引发的任何错误。

¥There is currently no built-in error handling in the Socket.IO library, which means you must catch any error that could be thrown in a listener.

io.on("connection", (socket) => {
socket.on("list items", async (callback) => {
try {
const items = await findItems();
callback({
status: "OK",
items
});
} catch (e) {
callback({
status: "NOK"
});
}
});
});

这可以重构为:

¥This can be refactored into:

const errorHandler = (handler) => {
const handleError = (err) => {
console.error("please handle me", err);
};

return (...args) => {
try {
const ret = handler.apply(this, args);
if (ret && typeof ret.catch === "function") {
// async handler
ret.catch(handleError);
}
} catch (e) {
// sync handler
handleError(e);
}
};
};

// server or client side
socket.on("hello", errorHandler(() => {
throw new Error("let's panic");
}));