Подтвердить что ты не робот

Какой самый чистый способ написать цикл без блокировки в javascript?

Итак, я думал о мозговой дразнилке - что, если бы у меня был большой объект, по какой-то причине мне пришлось перебирать в node js и не хотелось блокировать цикл событий, пока я делал что?

Здесь пример из верхней части моей головы, я уверен, что он может быть намного чище:

var forin = function(obj,callback){
    var keys = Object.keys(obj),
        index = 0,
        interval = setInterval(function(){
            if(index < keys.length){
                callback(keys[index],obj[keys[index]],obj);
            } else {
                clearInterval(interval);
            }
            index ++;
        },0);
}

Хотя я уверен, что есть другие причины, что это беспорядочно, это будет выполняться медленнее, чем обычный цикл, потому что setInterval 0 фактически не выполняет каждые 0 мс, но я не уверен, как сделать цикл с гораздо более быстрым процессом .nextTick.

В моих тестах я нашел, что этот пример занимает 7 мс для запуска, в отличие от нативного цикла (с проверкой hasOwnProperty(), регистрацией той же информации), которая занимает 4 мс.

Итак, что самый чистый/самый быстрый способ написать этот же код с помощью node.js?

4b9b3361

Ответ 1

Поведение process.nextTick изменилось с тех пор, как был задан вопрос. Предыдущие ответы также не отвечали на вопрос в соответствии с чистотой и эффективностью функции.

// in node 0.9.0, process.nextTick fired before IO events, but setImmediate did
// not yet exist. before 0.9.0, process.nextTick between IO events, and after
// 0.9.0 it fired before IO events. if setImmediate and process.nextTick are
// both missing fall back to the tick shim.
var tick =
  (root.process && process.versions && process.versions.node === '0.9.0') ?
  tickShim :
  (root.setImmediate || (root.process && process.nextTick) || tickShim);

function tickShim(fn) {setTimeout(fn, 1);}

// executes the iter function for the first object key immediately, can be
// tweaked to instead defer immediately
function asyncForEach(object, iter) {
  var keys = Object.keys(object), offset = 0;

  (function next() {
    // invoke the iterator function
    iter.call(object, keys[offset], object[keys[offset]], object);

    if (++offset < keys.length) {
      tick(next);
    }
  })();
}

Обратите внимание на комментарии @alessioalex относительно Kue и правильной очереди заданий.

Смотрите также: share-time, модуль, который я написал, чтобы сделать что-то похожее на намерение исходного вопроса.

Ответ 2

Здесь есть много вещей.

  • Если у вас есть веб-приложение, например, вы не захотите делать "тяжелый подъем" в этом процессе приложения. Несмотря на то, что ваш алгоритм эффективен, все равно, скорее всего, это замедлит работу приложения.
  • В зависимости от того, чего вы пытаетесь достичь, вы, вероятно, используете один из следующих способов:

    a) поставьте цикл "for in" в дочернем процессе и получите результат в своем основном приложении после его завершения
    б) если вы пытаетесь достичь чего-то вроде отложенных заданий (для отправки электронной почты), вы должны попробовать https://github.com/LearnBoost/kue
    c) сделайте собственную программу Kue, используя Redis для связи между основным приложением и приложением "тяжелая атлетика".

Для этих подходов вы также можете использовать несколько процессов (для concurrency).

Теперь время для примера кода (возможно, оно не идеально, поэтому, если у вас есть лучшее предложение, пожалуйста, исправьте меня):

var forIn, obj;

// the "for in" loop
forIn = function(obj, callback){
  var keys = Object.keys(obj);
  (function iterate(keys) {
    process.nextTick(function () {
      callback(keys[0], obj[keys[0]]);
      return ((keys = keys.slice(1)).length && iterate(keys));
    });
  })(keys);
};

// example usage of forIn
// console.log the key-val pair in the callback
function start_processing_the_big_object(my_object) {
  forIn(my_object, function (key, val) { console.log("key: %s; val: %s;", key, val); });
}

// Let simulate a big object here
// and call the function above once the object is created
obj = {};
(function test(obj, i) {
  obj[i--] = "blah_blah_" + i;
  if (!i) { start_processing_the_big_object(obj); }
  return (i && process.nextTick(function() { test(obj, i); }));
})(obj, 30000);

Ответ 3

Вместо:

for (var i=0; i<len; i++) {
  doSomething(i);
  }

сделайте что-нибудь вроде этого:

var i = 0, limit;
while (i < len) {
  limit = (i+100);
  if (limit > len)
    limit = len;
  process.nextTick(function(){
     for (; i<limit; i++) {
      doSomething(i);
     }
    });
  }
}

Это запустит 100 итераций цикла, а затем вернет управление системе на мгновение, а затем поднимет, где оно остановилось, до его завершения.

Изменить: здесь он адаптирован для вашего конкретного случая (и с количеством итераций, которые он выполняет в момент, переданный в качестве аргумента):

var forin = function(obj, callback, numPerChunk){
  var keys = Object.keys(obj);
  var len = keys.length;
  var i = 0, limit;
  while (i < len) {
    limit = i + numPerChunk;
    if (limit > len)
      limit = len;
    process.nextTick(function(){
        for (; i<limit; i++) {
          callback(keys[i], obj[keys[i]], obj);
        }
      });
  }
}

Ответ 4

В JavaScript [JavaScript] используется JavaScript; это может быть совершенно не имеет отношения к node.js.


Два варианта, о которых я знаю:

  • Используйте несколько таймеров для обработки очереди. Они будут чередовать, что даст чистый эффект "обработки элементов" чаще (это также хороший способ украсть больше CPU;-) или,
  • Выполняйте больше работы за цикл, будь то счет или время.

Я не уверен, что веб-рабочие применимы/доступны.

Счастливое кодирование.