ASP.NET SignalR のデモでよく見るような、ドラッグドロップでオブジェクトを動かして、それを全クライアントに配信するようなアプリケーションだと、そのまま実装するとサーバとクライアント間で数 ms 間隔で通信が行われてしまいます。
それは非常に効率が良くないということで、ASP.NET SignalR の公式ページには高頻度で通信が行われるアプリケーションの実装方法がまとめられています。
Tutorial: High-Frequency Realtime with SignalR 2 | The ASP.NET Site
この例ではクライアント側とサーバ側にメッセージループを実装して、最大でも 100ms 間隔でしか通信を行わないようにしています。
<script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0, top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); // Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script>
しかし、どうしても実装がごちゃごちゃしてしまって分かりにくいですね。そんな時に backburner.js というライブラリの紹介記事を見つけたので、これを使えば SignalR でのクライアント側は簡単に実装できると思いました。
クライアントサイド JavaScript のパフォーマンス改善には backburner.js が便利! - tricknotesのぼうけんのしょ
という訳で backburner.js について調べてみたんですが、NuGet にはまだ登録されていないようでした。
ところがはてなブックマークのコメントで同じことは underscore.js で出来ると書かれていたのと NuGet にも登録されていたので、今回は underscore.js を使ってみることにしました。underscore.js については以下の記事がわかりやすかったです。
便利機能満載のライブラリUnderscore.js - にのせき日記
今回は throttle というメソッドを使えば、同じことは出来そうですね。という訳で公式ページのサンプルを underscore.js を使って書き換えてみました。
<script src="Scripts/jquery-2.0.0.min.js"></script> <script src="Scripts/jquery-ui-1.10.3.min.js"></script> <script src="Scripts/jquery.signalR-1.1.1.min.js"></script> <script src="Scripts/underscore.min.js"></script> <script> $(function() { var connection = $.hubConnection(); var move = connection.createHubProxy("move"); var shape = $("#shape"); move.on("Receive", function(x, y) { shape.animate({ left: x, top: y }, { duration: 100, queue: false }); }); connection.start(function() { shape.draggable({ drag: _.throttle(function() { var offset = shape.offset(); move.invoke("Send", offset.left, offset.top); }, 100) }); }); }); </script>
今回は 100ms 毎に 1 回だけサーバと通信するようにしています。余計なコードが綺麗になくなって、本質的な部分だけ残りましたね。
drag した時の関数は高頻度に呼び出されるので、drag に underscore.js の throttle メソッドで作成した関数を渡しています。通信が間引かれた分、動きはカクカクしますがアニメーションを使って補完しました。
初めて underscore.js を使ってみたんですが、いろんな機能が用意されていて今まで損してたなーと思いました。まずは、バーチャル緑タイツマンのアップデートに期待しましょうか。