有没有办法使代码块本身不阻塞

Is there a way to make block of code non-blocking itself

本文关键字:代码 有没有      更新时间:2023-09-26

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

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

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

奖励问题:在JS必须做繁重工作的用例中,NodeJS的编码实践在客户端应用程序中是否站得住脚?

注意: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.
});

我在同一条船上。我有点喜欢 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 毫秒,因此,触发数千或数百万个计时器会无缘无故地变慢。 所以基本上,如果你需要在不锁定浏览器的情况下执行数千个或更多的循环,你需要更像一个线程(喘息),并在设定的时间内"刻录"一些代码,而不是设定迭代次数。