在JavaScript中编写非阻塞for循环的最简洁的方法是什么?

所以,我一直在想一个脑筋急转弯 – 如果我有一个很大的对象,我因为某种原因不得不在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毫秒执行,但我不知道如何做一个循环与多更快的process.nextTick。

在我的testing中,我发现这个例子需要7毫秒的时间来运行,而不是本地for循环(hasOwnProperty()检查,logging相同的信息),这需要4毫秒。

那么,使用node.js编写相同代码的最简洁/最快的方法是什么?

自问题提出以来, process.nextTick的行为已经改变。 以前的答案也没有按照function的清洁和效率来解决问题。

 // 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 ,我写的一个模块,用于执行与原始问题相似的模块。

这里有很多事情要说。

  • 例如,如果您有一个Web应用程序,那么您就不会在该应用程序的过程中“举重”。 即使你的algorithm是有效的,它仍然很可能会减慢应用程序。
  • 根据您要达到的目标,您可能会使用以下方法之一:

    a)把你的“for in”循环放入一个subprocess中,一旦完成就在你的主应用程序中得到结果
    b)如果您正在尝试实现类似延迟工作(用于发送电子邮件),您应该尝试https://github.com/LearnBoost/kue
    c)使用Redis制作一个类似Kue的程序,在主应用程序和“繁重”应用程序之间进行通信。

对于这些方法,您也可以使用多个进程(用于并发)。

现在是代码示例(可能不完美,如果你有更好的build议,请纠正我):

 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's 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); 

代替:

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

以下内容适用于[浏览器] JavaScript; 这可能与node.js完全无关。


我知道的两个选项:

  1. 使用多个定时器来处理队列。 他们会交错,这将产生“更频繁地处理项目”的净效果(这也是一个很好的方法来窃取更多的CPU ;-),或者,
  2. 每个周期做更多的工作,无论是数量还是时间。

我不确定Web Workers是否适用/可用。

快乐的编码。