WXHR:Web Workersの趣味を備えた古き良きXHR

Webアプリケーションがサーバーから大量のデータを取得し、デコードして宛先に送信する必要がある場合があります。 この例としては、各モデルがgzip'an json'eで数メガバイトを占有できるオンライン3Dエディターがあります。



平均的なユーザーのブラウザがデータをダウンロードおよび展開するときに1秒以上フリーズした場合はどうすればよいですか?

1.フラッシュ上で何かを思いつきます(100%確信はありませんが、一部のブラウザーはメインスレッドでプラグインを起動します)

2.データを分割してロードし、分割して処理します。

3.ユーザーにコンピューターのアップグレードを依頼します。



3つのオプションはすべて正しくありませんか?



カットの下で、この問題に対するエレガントなソリューション(不要なスクリプトとアプリコードなし)。



Webワーカーは私たちの助けになります。幸いなことに、内部にはxhrインターフェイスがあります。



古き良きXHRをワーカーでソルトする必要がありますが、XHRの古いバージョンを使用するスクリプトがスプーフィングに気付かないように、これを行う必要があります。 そして、古いブラウザは、労働者のサポートなしで、以前と同じように機能します。

基本として、Pro JavaScript Design Patterns(7.03-XHRファクトリーの例)のxhrスクリプトを使用します。

xhrスクリプトは、3つのモードで動作するはずです:古いブラウザのxhr、wxhrホスト、wxhrワーカー。



作業の論理は次のとおりです。

0.ユーザーはxhr.requestを実行します

1.ブラウザがワーカーを保持していない場合、以前と同様に作業します

2.ブラウザが保持している場合、リクエストメソッドでワーカー(wxhr.js)を生成し、リクエストを実行しません。

2.1ワーカーの適切なイベントを切断し、postMessageを介してワーカーにリクエストをプロキシします

3. wxhr.jsスクリプトはワーカーとして起動され、スクリプトはワーカーモードで動作していることを理解し、メッセージイベントをハングさせます。

3.1スクリプトはリクエストを受け取り、

3.2通常のxhrオブジェクトを作成し、

3.3通常のxhr.requestを実行し、

3.4データの処理、データのホストへの転送、

3.5ホストは、ワーカーによって処理されたデータを使用してコールバックを行います。



パッチを当てたxhr別名wxhr:

/** * @fileOverview WXHR Request - Web Worker XHR * * @example * <pre> * var myHandler = new global.xhr(true), // enable workers * myHandler2 = new global.xhr(), // worker mode is disabled by default * data = { * method: 'GET', * url: 'test.txt', * success: function (data, isWorker) { * alert(data + (isWorker ? ' XHR called from Worker' : ' XHR called from Window')); * }, * error: function (status) { * alert(status); * } * }; * * myHandler.request(data); * myHandler2.request(data); * </pre> * * @author azproduction */ /**#nocode+*/ (function (global) { /**#nocode-*/ // Upgrade 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - var xhr = function (canUseWorkers) { this.canUseWorkers = (typeof canUseWorkers === 'undefined') ? false : !!canUseWorkers; }, // detect workers support workersSupported = typeof global.Worker !== 'undefined', // detect mode itIsWindow = typeof global.document !== 'undefined'; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - xhr.prototype = { request: function(params) { params.method = params.method.toUpperCase() || 'GET'; params.url = params.url || ''; params.postVars = params.method !== 'POST' ? (params.postVars || null) : null; params.success = params.success || function () {}; params.error = params.error || function () {}; // Upgrade 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (this.canUseWorkers && workersSupported && itIsWindow) { // use advanced wxhr var worker; // this is bad part mb createObjectURL will save in future worker = new global.Worker('wxhr.js'); // <<< bad worker.onmessage = function(e) { var data = e.data; // proxy response // @todo delete true parameter in Production! params[data.callback](data.data, true); }; // if worker throws error query fails worker.error = function(e) { params.error(0); }; // worker proxy request worker.postMessage({ method: params.method, url: params.url, postVars: params.postVars }); return; } // browser do not support workers or script is already works as Worker // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var xhr = (this.createXhrObject())(), self = this; xhr.onreadystatechange = function() { try { if (xhr.readyState !== 4) { return; } self.processResponse(params, { status: xhr.status, responseText: xhr.responseText, contentType: xhr.getResponseHeader('Content-type') }); } catch (e) { params.error(xhr.status); } }; xhr.open(params.method, params.url, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.send(params.postVars); }, createXhrObject: function() { // Factory method. var methods = [ function() { return new XMLHttpRequest(); }, function() { return new ActiveXObject('Msxml2.XMLHTTP'); }, function() { return new ActiveXObject('Microsoft.XMLHTTP'); } ]; for (var i = 0, len = methods.length; i < len; i++) { try { methods[i](); } catch(e) { continue; } // If we reach this point, method[i] worked. this.createXhrObject = methods[i]; // Memoize the method. return methods[i]; } // If we reach this point, none of the methods worked. throw new Error('SimpleHandler: Could not create an XHR object.'); }, processResponse: function (params, xhr) { if (xhr.status === 200) { if (xhr.contentType.match(/^application\/json/)) { params.success(JSON.parse(xhr.responseText)); } else { params.success(xhr.responseText); } } else { params.error(xhr.status); } } }; // Upgrade 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (!itIsWindow) { // worker mode: listen for requests global.addEventListener('message', function(e) { var data = e.data; // proxy success data.success = function (data) { global.postMessage({ callback: 'success', data: data }); }; // proxy error data.error = function (status) { global.postMessage({ callback: 'error', data: status }); }; var xhrRequest = new xhr(); xhrRequest.request(data); }, false); } else { // script mode: export xhr global.xhr = xhr; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // global.xhr = xhr; /**#nocode+*/ }(this)); /**#nocode-*/
      
      





作業例: azproduction.ru/wxhr



どこでもwxhrを使用することはお勧めしません。大量の入力データの処理が必要な場合にのみ必要です。他の場合は、メインストリーム(ワーカーのスポーン、ダブルデータ転送)から常にxhrに劣ります。 一部のブラウザ、特にサファリでは、postMessageは送信前にjsonでデータをエンコードし、受信時にデコードします。 そのため、通常のxhrよりもさらに悪化する可能性があります。



テストを実施:キャッシュをバイパスして2つの同一のリクエストを開始し、最初にwxhr、2番目にxhrを送信しました。 クロムでは、wxhrの100%のケースでffとサファリが2番目に、オペラでは75%のwxhrが2番目に来ました。 データ6バイト+ヘッダー。



All Articles