News

お知らせ
  • 2018.06.12
  • blog
     
  • JavaScriptで頻繁に利用する並行処理について

  • 最近データビジュアライゼーションの流れで、
    ブラウザで大きいデータを表示することが増えました。
    その際、私が最も役に立っている事柄についてご紹介したいと思います。

    多数あるJavaScriptの書籍について幾つか読みましたが
    その中で最も役に立っていると感じているのが、
    jQueryの作者であるJohn Resigさんが書いた以下の書籍です。

    JavaScript Ninjaの極意 ライブラリ開発のための知識とコーディング (Programmer's SELECTION)
    JavaScript Ninjaの極意 ライブラリ開発のための知識とコーディング (Programmer’s SELECTION)

    この書籍の
    「第8章 スレッドとタイマの扱い方」の中で、
    「8-3 大きなタスクをタイマを使って処理する」
    という項目が非常に役に立っています。

    内容を要約すると、
    シングルスレッドであるJavaScriptで大きな処理を一気に行うと、
    画面レンダリング等の他のタスクが行われず、
    一切の処理が止まってしまったように見えたり、
    最悪の場合、画面が固まってしまうことがあるので、
    大きな処理を小さなタスクに分割して
    並行処理を行うようにするというものです。

    データビジュアライゼーションに当てはめて考えると、
    大きなデータを表示しようとした時にしばらく何も表示されず、
    描画のタスクが完了した時に
    一気に画面上に表示されるようなケースが該当すると思います。

    上記の書籍を参考に
    私なりに配列形式の大きなデータを分割する以下の関数を作成しました。

    /**
     * 配列をchunkSize毎に分割してコールバック関数を適用する
     *
     * @param {Array} orgArr 分割対象の配列
     * @param {number} chunkSize 一回の処理で行う要素の個数
     * @param {function} callback 配列の要素に対して適用したい関数
     * @param {function} lastCallback 配列の全要素に対する処理が終了した後に実行する関数
     */
    function prepareDividedArray(orgArr, chunkSize, callback, lastCallback) {
    
      var idx = 0;
      setTimeout(function () {
        doDividedArray(orgArr.slice(0, idx += chunkSize));
      }, 0);
    
      function doDividedArray(arr) {
        arr.forEach(callback);
        if (arr.length === chunkSize) {
          setTimeout(function () {
            doDividedArray(orgArr.slice(idx, idx += chunkSize));
          }, 0)
        } else {
          if (lastCallback) {
            lastCallback();
          }
        }
      }
    
    }
    

    どのように画面の動作が変わるのか
    beforeとafterのサンプルを作成しました。
    サンプルの内容としては、
    国土交通省からダウンロードした
    首都圏の行政区域をOpenStreetMapに描画しています。

    分割処理の適用前

    分割処理の適用後

    上記サンプルで読み込んでいるデータは数十MBとかなり大きいので、
    環境によっては動作が重くなってしまう可能性がありますので了承ください。