16

jQuery の各ループで「重い」キャンバス操作を行っているため、低速のデバイス (IE および iPad) が完全に応答しなくなることがあります。

だから私はアンダースコアを使用_.defer()して、次のように各ループで関数をキューに入れることができると考えていました。

function handleAsset = _.defer(function(){
//weightlifting goes here (partly async)
});

$.each(assets, handleAsset);

それでも、これは奇妙なエラーをスローします (スタック トレースは を指します$.each):

Uncaught TypeError: Object 20877 has no method 'call'

このアプローチは欠陥がありますか? これは、ハンドラー関数内で非同期操作が行われているためですか? これを達成するための別の/より良い方法はありますか?

4

2 に答える 2

22

欠陥があります。可能な限り低いポイントでコードを分離/分割するようにしてください。長期的には、ループの各反復を分離するだけで十分である可能性は低いと思います。

ただし、実際に行う必要があるのは、 UI キュー(またはUI スレッド)を更新するのに十分な余地を実装に与える非同期ランナウェイ タイマーをセットアップすることです。これは通常、(client)、(node.js)、(近日公開予定) などのメソッドを使用して行われます。setTimeout()nextTicksetImmediate

たとえば、配列があり、各エントリを処理したいとしましょう

var data = new Array(10000).join( 'data-' ).split('-'); // create 10.000 entries

function process( elem ) {
    // assume heavy operations
    elem.charAt(1) + elem.charAt(2);
}

for(var i = 0, len = data.length; i < len; i++ ) {
    process( data[i] );
}

このコードは、配列を反復処理してそのデータを処理する古典的なループです。また、100% の CPU 時間を消費するため、すべてのエントリを処理するのにかかる限り、ブラウザーのUI キューをブロックします (基本的に、ブラウザーの UI がフリーズして応答しなくなることを意味します)。

それを避けるために、次のような構成を作成できます。

var data  = new Array(10000).join( 'data-' ).split('-'); // create 10.000 entries

function runAsync( data ) {
    var start = Date.now();

    do {
        process( data.shift() );
    } while( data.length && Date.now() - start > 100 );

    if( data.length ) {
        setTimeout( runAsync.bind( null, data ), 100 );
    }
}

runAsync( data.concat() );

そこで何が起こるの ?

私たちが基本的に行っていることは次のとおりです。

  • 配列を取得し、 100 ミリ秒の時間枠内でできるだけ多くのデータ/エントリを処理します
  • その後、処理を停止し ( を呼び出しますsetTimeout)、UI に更新の機会を与えます
  • 配列にまだデータがある限り、それを行います

100 ミリ秒を超える遅延は、通常、人間の目によって「遅延」として認識されます。それ以下は流暢で素敵に見えます (少なくとも私たちの目はそう教えてくれます)。100ms は、最大処理時間の制限として適切な値です。50ミリ秒まで下げることをお勧めします。

ここでの注意点は、全体の処理時間が長くなるということですが、処理が速くなり、ユーザーエクスペリエンスが非常に悪くなる代わりに、処理を長くして応答性を維持する方が良いと思います.


クイックデモ:

于 2012-12-19T15:59:38.573 に答える
2

同時非同期操作の数を制限したいですか? 実装の欠点は、前のアクションが完了するまで各アクションを延期することです。

1 つのオプションは、シーケンス ヘルパーを使用することです。その後、このキューを処理用により管理しやすいチャンクに分割できます。

https://github.com/michiel/asynchelper-js/blob/master/lib/sequencer.js

var actions = [];
$.each(assets, function(key, value) {

    actions.push(function(callback) {
      $.ajax({
          url: 'process.php?id='+val,
          success: function(msg) {

            callback();
          }
        });
    });
  }
);

var sequencer = new Sequencer(actions);
sequencer.start();

アクション配列を 2 つの配列に分割し、それらを並べて実行すると、両方のキューが完了するまで、一度に実行されるプロセスは 2 つだけになります。

例えば

var arr1 = actions.splice(0,100);
var arr2 = actions.splice(100,200);

var sequencer1 = new Sequencer(arr1);
sequencer1.start();

var sequencer2 = new Sequencer(arr2);
sequencer2.start();
于 2012-12-19T16:20:48.210 に答える