有没有办法让代码块本身无阻塞

时间:2012-05-27 17:32:48

标签: javascript node.js

在经常使用节点之后,我不得不习惯以非阻塞的方式编写代码,但是我能做到这一点的主要方法是使用本身异步的函数。例如:stat(f,callback)forEach(array, callback)他们自动接受你给出的任何回调,我认为它是主要的执行高速公路,并在被叫后立即返回。

我想知道的是:如何告诉ECMA引擎以异步方式执行函数?

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

奖金问题:在JS必须做大量工作的用例中,NodeJS的编码实践是否在客户端应用程序中占有一席之地?

2 个答案:

答案 0 :(得分:3)

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

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

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

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

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

如果选择利用闭包,请谨慎使用自由变量,因为最终调用回调时变量可能会发生变异。

使用异步框架,例如Q by kriskowal(可在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.
});

答案 1 :(得分:0)

我在同一条船上。我有点喜欢Node的异步函数,所以我写了这个async 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毫秒,因此,无论如何,激活数千或数百万个定时器的速度会很慢。所以基本上,如果你需要在不锁定浏览器的情况下执行数千或更多循环,你需要更像一个线程(喘气),并在一段时间内“烧”一些代码,而不是设置迭代次数。 / p>