2

在大量使用 node 之后,我不得不习惯于以非阻塞方式编写代码,但是我可以做到这一点的主要方式是使用本身是异步的函数。例如:stat(f,callback)或者forEach(array, callback)他们会自动从我认为的主要执行高速公路中获取你给他们的任何回调,并在被调用后立即返回。

我想知道的是:我怎样才能告诉 ECMA 引擎异步执行一个函数,不管它是什么?

我的特殊用例涉及在 DOM childList 上迭代 for 循环以解析数千个元素;我的问题是所有其他元素都是我想跳过的文本节点。虽然我会使用forEach()这不是最好的,但我只看到for(a,i=0;a=table[i];i=i+2){/*process 'a'*/}能够纠正它,但代价是被阻塞。最好的行动方案是什么?

额外的问题: NodeJS 的编码实践在 JS 必须进行繁重工作的用例中是否在客户端应用程序中站稳脚跟?

4

2 回答 2

3

注意:Array.prototype.forEach是同步的,不是异步的。JS 标准(ECMAScript 第 5 版)中定义的任何内容都不能是异步的,因为该标准没有定义异步语义(Node.js 和 DOM 有)。

您可以使用setTimeout(在浏览器和 Node.js 中工作)或process.nextTick(特定于 Node.js):

for (...) {
    doWorkAsync(...);
}

function doWorkAsync(...) {
    setTimeout(doWorkSync.bind(null, ...), 0);
}

function doWorkSync(...) {
    ...
}

如果您选择利用闭包,则在使用自由变量时要小心,因为最终调用回调时变量可能会发生变化。

使用异步框架,例如kriskowal 的 Q(可跨 Node.js 和现代浏览器移植),您可以进行 mapreduce 风格的编程:

var Q = require('q');  // npm package 'q'

function getWorkloads() {
    var workloads = [ ];
    for (...) {
        workloads.push(Q.fcall(doWorkSync.bind(null, ...)));
    }
    return workloads;
}

Q.all(getWorkloads()).then(function (results) {
    // results array corresponds to
    // the array returned by getWorkloads.
});
于 2012-05-27T17:47:07.873 回答
0

我在同一条船上。我有点喜欢 Node 的异步函数,所以我写了这个异步 For 和 ForEach 函数。它使用“setTimeout(Func,0);” 诡计。

这是图书馆:

  var WilkesAsyncBurn = function()
  {
    var Now = function() {return (new Date());};
    var CreateFutureDate = function(milliseconds)
    {
      var t = Now();
      t.setTime(t.getTime() + milliseconds);
      return t;
    };
    var For = function(start, end, eachCallback, finalCallback, msBurnTime)
    {
      var i = start;
      var Each = function()
      {
        if(i==-1) {return;} //always does one last each with nothing to do
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=end) {i=-1; finalCallback(); return;}
          eachCallback(i);
          i++;
        }
      };
      Each();
    };
    var ForEach = function(array, eachCallback, finalCallback, msBurnTime)
    {
      var i = 0;
      var len = array.length;
      var Each = function()
      {
        if(i==-1) {return;}
        setTimeout(Each,0);
        var burnTimeout = CreateFutureDate(msBurnTime);
        while(Now() < burnTimeout)
        {
          if(i>=len) {i=-1; finalCallback(array); return;}
          eachCallback(i, array[i]);
          i++;
        }
      };
      Each();
    };

    var pub = {};
    pub.For = For;          //eachCallback(index); finalCallback();
    pub.ForEach = ForEach;  //eachCallback(index,value); finalCallback(array);
    WilkesAsyncBurn = pub;
  };

示例用法:

  WilkesAsyncBurn(); // Init the library
  console.log("start");
  var FuncEach = function(index)
  {
    if(index%10000==0)
    {
        console.log("index=" + index);
    }
  };
  var FuncFinal = function()
  {
    console.log("done"); 
  };
  WilkesAsyncBurn.For(0,2000000,FuncEach,FuncFinal,50);

打印:索引=10000 索引=20000 索引=30000 等“完成”

如果有兴趣,请进行更多研究:

setTimeout 和 setInterval 的最小开销时间约为 2 到 10 毫秒,因此,触发数千或数百万个计时器将无缘无故地变慢。所以基本上,如果您需要在不锁定浏览器的情况下执行数千次或更多循环,您需要更像一个线程(喘气),并在设定的时间内“烧掉”一些代码,而不是设定的迭代次数。

于 2013-03-29T20:32:35.713 回答