🐢
[js] 手続き的 な WebSocket ラッパーをつくろう
^.,.^ < Hey! 青色きつねです。
VSCode拡張機能のために書いたはいいものの結局使わなかった。せっかくなのでブログのネタにしちゃう。
websocketは現状イベントハンドラを使って処理を組んでいくので、例えば以下のように手続き的な書き方が出来ません。
socket.send("message");
let buf = socket.recv();
要はこう書きたくてawaitするモジュールを書きました。
特に目新しいことは何もないんですが、promiseの勉強の参考になるかもしれません。
イベント駆動型を手続き型にラップしたり、その逆もというのはpromiseの登場で容易になりました。らしい。(コールバック地獄の回避のほうが話題ですが)そんな現代を生きる野生動物はそんな乱暴な事をたまにやるので、メモとして残しておきます。
ソース
使い方
let awaitbleWebSocket = await (new AwaitbleWebSocket("ws:127.0.0.1:5001"));
let response = await s.send("Bello");
// { message: 'Bello', uuid: 'af6ec486-9c81-4e7b-b42a-0ea4950bac38' }
クライアント:
サーバー:
const ws = require("ws");
let WebSocketServer = new ws.Server({ port: 5001 });
WebSocketServer.on("connection", (webSocket) => {
webSocket.on("message", (message) => {
message = JSON.parse(message);
console.log(message);
WebSocketServer.clients.forEach((client) => {
client.send(JSON.stringify(message));
});
});
webSocket.on("close", () => {
console.log("close");
});
});
せつめいしょ
まずはコンストラクタです。
分かりやすいようにコードをちょっと削ってみます。
class AwaitbleWebSocket {
constructor(url) {
this.socket = new WebSocket(url);
this.isOpen = false;
let _resolve_ = () => { };
this.socket.addEventListener("open", (event) => {
this.isOpen = true;
_resolve_(this);
});
return new Promise((resolve, reject) => {
// 仕事でやると怒られそうな書き方
this.isOpen ? resolve(this) : _resolve_ = resolve;
});
}
}
await(new AwaitbleWebSocket(url))
でconstructorがpromiseを返し、接続がopenになるまでボーっとします。
タイミング次第でresolveが電子の海に消えるので、もしかしたらここはintervalを組んだほうがいいかもしれません。どっちでもいいやい
return new Promise((resolve, reject) => {
// 仕事でやると怒られそうな書き方
let interval_id = setInterval(() => {
if (this.isOpen) {
resolve(this);
clearInterval(interval_id);
}
}, 10);
});
肝心の送信部分。関数な書き方をするには、どの送信がどの返信なのかを引き当てする必要があります。
仕組みは単純で、uuidを付加して送って返信にもそのまま含めてもらうだけです。
class AwaitbleWebSocket {
constructor(url) {
this.socket = new WebSocket(url);
this.messagePool = {};
this.socket.addEventListener("message", (event) => {
let data = JSON.parse(event.data);
if (data.uuid in this.messagePool) {
this.messagePool[data.uuid](event);
delete this.messagePool[data.uuid];
}
});
}
async send(message) {
let uuid = crypto.randomUUID();
this.socket.send(
JSON.stringify(
Object.assign(
{ message: message },
{ uuid: uuid }
)
)
);
let R = new Promise((resolve, reject) => {
this.messagePool[uuid] = (_) => {
resolve(JSON.parse(_.data));
};
});
return R;
}
}
Social
Discussion