Как я могу отложить jQuery каждый цикл

Я делаю "тяжелые" операции с холстом в 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'

Этот подход ошибочен? Это связано с тем, что операции async выполняются внутри функции обработчика? Есть ли другой/лучший способ достичь этого?

Ответы

Ответ 1

Это неверно. Вы должны попытаться разделить/разбить код в самой низкой точке. Я думаю, что его маловероятно, что просто разделить каждую итерацию цикла достаточно в долгосрочной перспективе.

Однако вам действительно нужно настроить асинхронный таймер убегания, который дает возможность реализовать достаточно места для обновления очереди UI (или потока пользовательского интерфейса). Обычно это делается с помощью таких методов, как setTimeout() (клиент), nextTick (node.js) или setImmediate (скоро).

Например, скажем, у нас есть массив, и мы хотим обработать каждую запись

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% процессорного времени и, следовательно, блокирует очередь пользовательских интерфейсов браузера, если требуется, чтобы обрабатывать все записи (что в основном означает, что пользовательский интерфейс браузера замерзнет и перестанет реагировать).

Чтобы этого избежать, мы могли бы создать такую ​​конструкцию:

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 мс обычно распознается глазами человека как "отставание". Все, что ниже этого, кажется плавным и приятным (по крайней мере, наши глаза расскажут нам об этом). 100 мс является хорошим значением в качестве предела для максимального времени обработки. Я даже предлагаю спуститься до 50 мс.

Опасность здесь заключается в том, что общее время обработки увеличится, но я думаю, что лучше иметь более длительную обработку и оставаться отзывчивым, вместо этого быстрее обрабатывать и очень плохой пользовательский интерфейс.


Быстрая демонстрация:

Ответ 2

Итак, вы хотите ограничить количество одновременных асинхронных операций? Недостатком вашей реализации является то, что вы будете откладывать каждое действие до тех пор, пока предыдущий не завершится.

Один из вариантов заключается в использовании помощника последовательности, тогда вы можете разбить эту очередь на более управляемые куски для обработки.

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();

Если вы разделите массив действий на два массива и попросите их работать бок о бок, тогда у вас будет только два процесса, которые будут выполняться одновременно, пока обе очереди не будут завершены.

например.

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();