遅延配列処理

かつて、JavaScriptで複雑なアプリケーションを開発すると、克服できない問題が発生しました-Internet Explorer(以降IE)の大きな配列。 Chromeで最初に動作をテストしたので、アレイを使用するときに問題に気付きませんでした。 私はもっ​​と言います-ネストされた配列は恐怖感を引き起こしませんでした。 Chromeは困難なタスクに簡単に対処しました。 しかし、IEは非常に厳しい形式で、コードのすべての欠陥と欠陥、特に大規模な配列の処理を指摘しました。



一言で言えば



開発したアプリケーションは、サーバーからのイベントにリアルタイムで応答する必要がありました。 これらの目的のために、 Socket.IOライブラリを使用しました 。これにより、サーバーとの双方向通信を簡単に実装できました。 同時に、アプリケーションはサードパーティのサービスにも結び付けられていたため、外部のREST APIを介してのみアクセスできました。



弱いリンクは、サードパーティサービスから受信したデータの配列を処理することになっていた大きなサイクルでした。 この場合、サイクルでDOMモデルを同時に変更する必要がありました。 これらの場合、IEは深い思考の状態になり、自然に接続を切断しました。 外側では、これは受け入れられませんでした-アプリケーションをブロックし、常に回転するダウンロードアイコンは顧客を満足させることができませんでした。 はい、多くの人がDOMモデルの変更にかかる高いコストを正しく指摘できます。 そして、サイクルの外側ですべての変更を行うことにより、問題のこの部分が解決されたことを保証しなければなりません。 ただし、主な問題-配列処理中の接続の切断は残りました。



何千行ものコードがすでに書かれていたため、完全なリファクタリングは怖いものでした。 さらに、締め切りが迫っていました。 このような条件下で、配列の「遅延」部分処理のアイデアが生まれました。



アルゴリズム



配列はいくつかの範囲に論理的に分割され、各範囲は遅延して処理されます。 作品の本質を図に示します:







したがって、単一のグローバル配列を持つ場合、関数は事前に決められた遅延で、部分的に処理します。 JavaScriptである非同期言語の場合、この手法を使用すると、非常に大きな配列を処理するときにメインスレッド(これも1つだけ)をオフロードできます。



ハンドラーの本体:

//Lazy array processing /** * Input parameters * params = { * outerFuntion : function(array, start, stop), * mainArray: [] * startIndex: Int, * stepRange Int, * timeOut: Int, * callback: function() *} */ var lazyProcessing = function(params) { var innerFunction = (function(_) { return function() { var arrayLength = _.mainArray.length, stopIndex = _.startIndex + _.stepRange; if(arrayLength < stopIndex) { _.outerFunction( _.mainArray, _.startIndex, arrayLength - _.startIndex); if(_.callback != undefined) { _.callback(); } return; } else { _.outerFunction( _.mainArray, _.startIndex, stopIndex); _.startIndex += _.stepRange; lazyProcessing(_); } } })(params); setTimeout(innerFunction, params.timeOut); }; //Outer function works with mainArray in given range var func = function(mainArray, start, stop) { //TODO: this should be everything you want to do with elements of for (var i = start; i < stop; ++i) { // do something } };
      
      





lazyProcessing関数の本体では、ローカルのinnerFunction関数が作成され 、受信したparamsパラメーターが閉じられます。 これにより、 独自にlazyProcessing関数を再帰的に呼び出すたびに、一意のパラメーターを保存できます。



innerFunction関数は、次の非常に単純なアクションを実行する名前のない関数を返します。

  1. グローバル配列の終わりを確認します
  2. さまざまな停止値で外部関数outerFuntionを呼び出します
  3. 配列の最後に到達すると、 コールバック関数を呼び出します
  4. それ以外の場合は、 lazyProcessingを再帰的に呼び出します。


配列自体は、外部関数outerFuntion (グローバルな性質のため、実行できなかったが、これは視覚化に影響します)、およびループの開始と終了のインデックスに渡されます。 この場合、処理結果は配列または他のグローバル変数に保存できます。 それはすべて現在のタスクに依存します。



配列の最後に到達すると、コールバック関数が呼び出されますが、これはオプションです。



長所と短所



当然、このソリューションには落とし穴と欠点があります。

  1. 十分な大きさのmainArray配列を使用してstepRangeを最小に設定すると、スタックオーバーフローを無視できるようになります。
  2. 外部outerFunctionが呼び出されると、スレッドは引き続きブロックされます。 つまり パフォーマンスは、配列要素を処理するアルゴリズムに直接依存します
  3. ネストされて返された関数の「ヌードル」は非常にフレンドリーに見えません


同時に、一定の間隔で配列を部分的に処理しても、プログラムの実行の流れは妨げられません。 これにより、他のコールバック関数を処理できます。



完全な実例:
 //Test array var test = []; for(var i = 0; i < 100000; ++i) { test[i] = i; } //Lazy array processing /* params = { outerFuntion : function(array, start, stop), mainArray: [] startIndex: Int, stepRange Int, timeOut: Int, callback: function() } */ var lazyProcessing = function(params) { var _params = params; var innerFunction = (function(_) { return function() { var arrayLength = _.mainArray.length, stopIndex = _.startIndex + _.stepRange; if(arrayLength < stopIndex) { _.outerFunction( .mainArray, _.startIndex, arrayLength - _.startIndex); if(_.callback != undefined) { _.callback(); } return; } else { _.outerFunction( _.mainArray, _.startIndex, stopIndex); _.startIndex += _.stepRange; lazyProcessing(_); } } })(_params); setTimeout(innerFunction, _params.timeOut); }; //Test function works with array var func = function(mainArray, start, stop) { //TODO: this should be everything //you want to do with elements of mainArray var _t = 0; for (var i = start; i < stop; ++i) { mainArray[i] = mainArray[i]+2; _t += mainArray[i]; } }; lazyProcessing({ outerFunction: func, mainArray: test, startIndex: 0, stepRange: 1000, timeOut: 100, callback: function() { alert("Done"); } });
      
      









PS。 ユーザーzimorodokは素晴らしい例を挙げました-精神と本質において同じです。 追加できません。

timeOutを設定する機能を持つ各要素のコールバックで配列を渡します。

 /* example of use var arr = ['masha','nadya', 'elena']; iterate_async(arr, function (el, index, arr) { console.log(el + ' is #' + (index + 1)); }, 99); */ function iterate_async (arr, callback, timeout) { var item_to_proceed; item_to_proceed = 0; (function proceed_next () { if (item_to_proceed < arr.length) { setTimeout(function () { callback.call(arr, arr[item_to_proceed], item_to_proceed, arr); item_to_proceed += 1; proceed_next(); }, timeout || 50); } }()); }
      
      






All Articles