2012-05-27 43 views
2

很多使用节点后,我不得不习惯以非阻塞的方式编写我的代码,但是我可以做到这一点的主要方式是使用函数它们本身就是异步的。例如:stat(f,callback)forEach(array, callback)他们会自动采取您所给予的任何回拨,并将其视为主执行高速公路,并在被调用后立即返回。有没有办法使代码块本身非阻塞

我想知道的是:我怎么能告诉ECMA引擎执行的函数异步nomatter它是什么?

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

红利问题: NodeJS的编码实践是否在客户端应用程序中持有任何理由,因为JS需要进行繁重的工作。

回答

3

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

你可以(在浏览器和Node.js的中作品)使用setTimeoutprocess.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. 
}); 
+0

感谢您的澄清,谢谢! – TERMtm

+0

虽然快速问题:广泛使用setTimeout,安全还是昂贵? – TERMtm

+0

如果你正在使用超时时间为'0'的'setTimeout',你应该改用[process.nextTick'](http://nodejs.org/docs/latest/api/process.html#process_process_nexttick_callback)。 “在事件循环的下一个循环中调用此回调函数,这不是setTimeout(fn,0)的简单别名,它更有效率。” –

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毫秒,因此,释放数千或数百万个定时器的速度将无限缓慢。所以基本上,如果你需要在不锁定浏览器的情况下执行数千或更多的循环,你需要更像是一个线程(gasp),并将某些代码“烧”一段时间,而不是设置迭代次数。

+0

感谢您发布您的答案!你会考虑缩减你的代码示例,只是为了解释手头的问题吗?事实上,很难真正“获得”答案的本质。另外,在代码注释之外加入解释通常是有帮助的,因为它们可能更难以阅读(并且人们经常忽略它们) –