跳到主要內容
版本:4.x

聆聽事件

有許多方法可以處理在伺服器和用戶端之間傳輸的事件。

EventEmitter 方法

在伺服器端,Socket 實例會延伸 Node.js EventEmitter 類別。

在用戶端,Socket 實例會使用 component-emitter 函式庫提供的事件發射器,它會公開 EventEmitter 方法的子集。

socket.on(eventName, listener)

listener 函式新增到名為eventName 的事件的 listeners 陣列的結尾。

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

socket.once(eventName, listener)

為名為eventName 的事件新增一個一次性listener 函式

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

socket.off(eventName, listener)

從事件名稱為 eventName 的監聽器陣列中移除指定的 監聽器

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

socket.on("details", listener);

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

socket.removeAllListeners([eventName])

移除所有監聽器,或移除指定 eventName 的監聽器。

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

萬用監聽器

自 Socket.IO v3 起,一個新的 API 靈感來自 EventEmitter2 函式庫,允許宣告萬用監聽器。

此功能在用戶端和伺服器上皆可用。

socket.onAny(listener)

新增一個監聽器,當任何事件發出時,此監聽器將會觸發。

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

確認不會被萬用監聽器捕獲。

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

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

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

socket.prependAny(listener)

新增一個監聽器,當任何事件發出時,此監聽器將會觸發。此監聽器會被新增到監聽器陣列的開頭。

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

socket.offAny([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)

註冊一個新的萬用監聽器,用於接收封包。

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

確認不會被萬用監聽器捕獲。

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)

註冊一個新的萬用監聽器,用於接收封包。此監聽器會被新增到監聽器陣列的開頭。

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

socket.offAnyOutgoing([listener])

移除先前註冊的監聽器。如果未提供監聽器,則會移除所有萬用監聽器。

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

socket.onAnyOutgoing(listener);

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

// remove all listeners
socket.offAnyOutgoing();

驗證

事件參數的驗證不在 Socket.IO 函式庫的範圍內。

JS 生態系統中有許多套件涵蓋此用例,其中包括

使用 joiacknowledgements 的範例

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"
});
});

});

錯誤處理

Socket.IO 函式庫中目前沒有內建錯誤處理,這表示您必須捕捉監聽器中可能引發的任何錯誤。

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

這可以重構成

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");
}));