看到 [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[@PeakJi](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](/user/PeakJi)](http://weibo.com/peakji) 一条微博
node.js 0.9之后,任何异步递归都应用setImmediate而非process.nextTick!setImmediate和process.nextTick的区别是:前者将defer到队列末,且不会生成call stack;而后者是defer到该函数结束后执行,且process.nextTick用于递归会报警并最后爆盏…至于setInterval(func,0)就别用了,那是浏览器技巧。
地址: http://weibo.com/1811148004/zxJaEh6jK
觉得这个 API 好陌生… 搜索到的东西不多… https://github.com/NobleJS/setImmediate 可以细化一下么?
就字面上理解,就是说 setImmediate 是把那个调用放到了队列中去,而 process.nextTick 并没有把调用放到队列中,只是保存在了某个地方,等待调用 process.nextTick 的那个函数结束后,再自动执行。
忽略我…
这里说的setImmediate不是指Github上那个setImmediate.js,而是一个新的API。 你可以在最新版本的node下试试如下的代码:
function recurse(i,end){
if(i>end)
{
console.log('Done!');
}
else
{
console.log(i);
process.nextTick(recurse(i+1,end));
}
}
recurse(0,99999999);
执行几次后马上就 RangeError: Maximum call stack size exceeded
然后换用setImmediate:
function recurse(i,end){
if(i>end)
{
console.log('Done!');
}
else
{
console.log(i);
setImmediate(recurse,i+1,end);
}
}
recurse(0,99999999);
就完全没问题!
这是因为setImmediate不会生成call stack,异步的递归必须用它。
我认为说的不正确,无论是setImmediate还是process.nextTick的递归调用都不会造成栈溢出,如果是同步的递归,必须保存调用栈,而异步的递归根本不存在调用栈。之所以会发生Maximum call stack size exceeded,因为process.maxTickDepth的缺省值是1000,如果递归调用nextTick只能调用1000次,超过1000就会报这个错,但并不是真正栈溢出,只是想给你一个提示不希望你递归调用nextTick太多次,如果nextTick递归调用,那么其他的回调事件就会等待,会造成event loop饥饿,所以官方推荐用setImmediate作为递归调用,为什么setImediate,nextTick一个造成不饥饿,一个造成饥饿呢?那就要说两者的区别,可以看我的另一个回答。
首先同意@nodehugo所说,nextTick的栈溢出并不是真正的栈溢出,而是人为加上的东西,当初的讨论在这里 https://github.com/joyent/node/pull/3709 和 https://github.com/joyent/node/pull/3723
抛却maxTickDepth的问题,nextTick 和 setImmediate 主要的区别在于任务插入的位置
nextTick 的插入位置是在当前帧的末尾、io回调之前,如果nextTick过多,会导致io回调不断延后 setImmediate 的插入位置是在下一帧,不会影响io回调
我这人最大的问题就是看不得格式混乱,好吧,帮你整理了下 第一段:
function recurse (i, end) {
if (i > end) {
console.log('Done!');
} else {
console.log(i);
process.nextTick(recurse(i+1, end));
}
}
第二段;
function recurse (i, end) {
if (i > end) {
console.log('Done!');
} else {
console.log(i);
setImmediate(recurse, i+1, end);
}
}
可以这样来理解. process.nextTick和setImmediate方法在实现上分别对应这两个队列-- 不妨叫作nextTick队列和immediate队列. 到nextTick的时候, 这两个队列被执行的情况有所差异: nextTick队列中的方法会被全部执行(包括在执行过程中新加的),而immediate队列只会取其第一个方法来执行.
function nextTick(msg, cb) { process.nextTick(function() { console.log('tick: ' + msg); if (cb) { cb(); } }); }
function immediate(msg, cb) { setImmediate(function() { console.log('immediate : ' + msg); if (cb) { cb(); } }); }
nextTick(‘1’); nextTick('2’, function() { nextTick(‘10’); });
immediate('3’, function() { nextTick(‘5’); });
nextTick('7’, function() { immediate(‘9’); });
immediate('4’, function() { nextTick(‘8’); });
这段代码的输出是:
tick: 1 tick: 2 tick: 7 tick: 10 immediate : 3 tick: 5 immediate : 4 tick: 8 immediate : 9
解释如下:
1). 第一遍执行时, 会分别向nextTick队列和immedidate队列中加入方法,它们变成:
nextTick: 1 2 7 数字代表输出相应数字的那个nextTick方法对应的callback方法,下同
immedidate: 3 4
2). 到了nextTick, 开始执行回调
先执行 nextTick 队列中的回调(全部执行才结束):
2.1) 执行1 – 输出1
, nextTick队列变为 2 7
2.2) 执行 2 – 输出2
, 并向nextTick队列添加10, nextTick队列变为 7, 10
2.3) 执行7 – 输出7
, 并向immediate队列添加9. nextTick 队列变为 10, immediate队列变为 3 4 9
2.4) nextTick 队列中还有一个新添加的10, 故执行它, 输出10
再执行 immediate队列的第一个回调方法(immediate队列为 3 4 9), 即执行3. 3的执行,输出3
同时向nextTick队列中加入5 — 5 不会在这个tick执行, 因为本轮nextTick的执行已经结束了. 此时, 队列变为:
nextTick: 5
immedidate: 4 9
这也是进入下一轮tick前的队列状态.
3). 到了nextTick, 开始执行回调 队列为: nextTick: 5 immedidate: 4 9
和上一轮类似:
3.1) 执行 nextTick 5, 输出 5
. nextTick为空, 执行完毕.
执行immediate队列的第一个回调, 即4, 输出4
, 并向nextTick队列加入8. 此时队列变化为:
nextTick: 8
immediate: 9
4). 到了nextTick, 开始执行回调
根据上述原理,不难知道 将分别执行 nextTick 8 和 immediate 9 , 将输出8
和输出9
.