2011年11月13日 星期日

自認帥氣的 WebSocket 簡單命令處理模型

Standard
最近參與的一個 Project,需要在 WebSocket 下實作許多機制,這著實讓人傷透腦筋。又由於 JavaScript 完全是非同步(asynchronous)的設計概念,若遇到必需等待 Server 回應後才能繼續執行的工作,處理起來可是一件異常麻煩的事。雖然很熟悉運用回調函數(Callback Function)和暱名函數(Anonymous Function)來做,但一層包一層的設計,總讓人覺得進入無限階層的夢境地獄,什麼時候醒來都不知道。

這是筆者要達成的需求:『發送自己定義的命令去 Server,然後等待 Server 回應,一旦收到 Server 回應就可以繼續往下處理。』

一般的情況下,多數人都會這樣做(這理使用 Socket.IO 來建立 WebSocket,並和 Server 要當前時間為例):
/* Define handler to receive response from server */
socket.on('Clock', function(data) {
    /* Do something */
    alert(data);
});

/* Send command to server */
socket.emit('Clock', 'Time');

可是,並非每次對 Server 的請求,都希望透過 alert() 跳出結果,我們有時只是想要利用 Server 給的時間做一些其他處理,雖然是下同樣的命令,但後續處理完全不一樣。於是,筆者,設計了這樣的方式,簡單的去處理這種問題:
var command = {
    'Time': [],
    'Date': []
    /* You can define more commands */
};
socket.on('Clock', function(data) {
    if (command[data.command].length) {
        for (var index in command[data.command])
            command[data.command][index](data.result);

        command[data.command] = [];
    }
});

/* API to Send command to server */
function sendCommand(domain, cmd, callback) {
    command[cmd].push(callback);
    if (command[data.command].length == 1) {
        socket.emit(domain, cmd);
    }
}

/* New method to send command and show result in alert window */
sendCommand('Clock', 'Time', function(data) {
    alert(data);
});

/* New method to send command and set window title with result */
sendCommand('Clock', 'Time', function(data) {
    window.title = data;
});

這邊需要在 Server-side 改變回送的格式,告知 Client 現在回應的結果是屬於哪一個命令:
data = {
    command: 'Time',
    result: '11:11:11'
};

這樣的命令處理模型,適合用在讀取即時性低的公用狀態或常數,因為這種做法只會送出一個命令給 Server,程式在發送命令時就會自動合併當下發出的『同樣要求』,直到 Server 回應後,同時間一併觸發所有相應的 callback function。除了能減少傳送的資料量,也避免與 Server 的溝通等待,提升一些效能。

後記

若是不希望合並要求,且想要明確等到自己發送的命令回應後,才觸發自己的 callback function,廣泛的做法是建立計數器,但這種方法就不在本文討論的泛疇。