見出し画像

非同期通信を Web Worker で実行する

XMLHttpRequest による非同期通信、いわゆる Ajax は昨今のウェブサイトでは使用されないことの方が珍しいと言えます。

JavaScript は基本的にはシングルスレッドで実行されるため、非同期と言いつつも Ajax を行っている間は多少なりともリソースを喰ってしまいます。

実行する頻度が低ければあまり気にする必要もないかもしれませんが、現在ブラウザの JavaScript が担う役割はウェブサイトのリッチ化に伴い飛躍的に大きくなってきており、

なかでも WebGL などを利用した高度な描画表現を JavaScript で処理していたり、スクロールをトリガーとする演出表現を行っている間は、処理落ちを起こすと体感に直結するため、なるべくその他の処理でリソースを割きたくないのが実情です。

また動的ページ遷移を実現する Pjax やメッセージングアプリのように、 Ajax を多用するウェブサイトにおいても、塵も積もれば山となるでパフォーマンスに無視できない影響を与えます。

Web Worker に非同期通信を任せられないか

Web Worker は JavaScript にサブスレッドの処理を実現させ、メインスレッドの処理に負荷をかけることなく実行することのできるインターフェイスで、実のところモダンブラウザでほぼサポートされています。

もしこの Web Worker を利用してサブスレッドに Ajax の処理を任せることができれば、メインスレッドのリソースには余裕が生まれることになります。

…というアイデアを思いついて色々と調査したところ、Quiitaに既に実装を検討された記事があることを知りました。2013年の記事なのでもう9年も前なのですね。その頃の僕は Web Worker の存在すら知らなかったはずなので凄いとしか申し上げられません…。

ただし、ウェブサイトの実装に使おうとしたところ

  • コードの誤りが存在する

  • 戻り値となるデータの整形処理がない

  • エラー処理のハンドリングに不足がある

などで実際にはうまく動作しなかったり、都合に合わない部分がありましたので、おおまかにはここで書かれているコードの設計を参考にさせていただき、自分の使い勝手に合わせた形に編集してみました。

ajaxWorker

GitHub 上に公開しましたので、必要な方はご利用いただくなり、フォークして機能追加していただくなどしていただいて構いません。

利用方法については GitHub 上の README.md を参照いただければと思いますが、メインスレッドとなる JavaScript 上で

ajaxWorker({
    url: 'http://example.com/api',
    dataType: 'json',
    success: function (_data) {
    
    },
    error: function (_status, _statusText, _message) {
     
    },
    complete: function () {

    }
});

とするだけで、サブスレットで Ajax が実行される仕組みになっています。

取得可能なデータは テキスト, JSON, XML, 画像, Data URI の5種類で、大抵の用途には事足りるかなと思います。

細かなことを言えば、XMLや画像をオブジェクトとして整形する処理はメインスレッド上で展開されるのですが、これは Web Worker のサブスレットでは DOM や XMLHttpRequest の responseXML などを扱えない仕様上の制約があるためです。サブスレッドで可能な部分だけ最大限処理しています。

画像エレメント

画像 の取得をした場合には、戻り値として Data URIによるインラインデータの画像エレメントと Blobデータの画像エレメント の2通りを選択可能にしました。

ajaxWorker({
    url: 'http://example.com/assets/images/example.jpg',
    dataType: 'image',
    elementType: 'blob',
    success: function (_data) {
        
    },
    error: function (_status, _statusText, _message) {
        
    },
    complete: function () {
        
    }
});

success への戻り値として既に onload 後の画像エレメントが入っているので、その後の処理でそのまま

document.querySelector('#id').appendChild(_data);

などすれば HTML 内に追加できるはずです。

実際に Web Worker で動作しているの?

見た目にはまったく変化がないので不安になるところですが、現在稼働中のスレッドは Chrome の DevTools にある Sources パネルで確認することができます。

ajaxWorker で非同期通信を行なった場合ここに Main 以外のサブスレッドとして一瞬ですが 54fd6dd9-9eff-4f72-ab-57-......... のようなランダムな文字列のサブスレット名が現れることが分かります。

* 表示が一瞬なのは、処理終了後にスレッドを閉じるように設計しているからで、もし残り続けるようならエラーやバグが発生している可能性があります。

読んでいただきありがとうございます。丁寧な記事作りをこころがけていますので、記事が気に入ったなどでカンパをよせていただけるのなら励みになります。