setTimeout 和 process.nextTick 递归调用为什么不会栈溢出?
发布于 12 年前 作者 jiyinyiyong 6454 次浏览 最后一次编辑是 8 年前

代码

// Generated by CoffeeScript 1.4.0
var a, f, log;

a = 1;

log = console.log;

f = function(b) {
  log(b);
  return process.nextTick(function() {
    return f(b + 1);
  });
};

f(a);

居然就这么执行了… 不是说 JS 没有尾递归优化的么, 而且作用域里的 b 不是也要保存的么… 可是没有栈溢出的报错

9 回复

如果不用process.nextTick,为什么递归次数会出现以下结果: 1.如果是保存为文件,运行:永远是在17956次的时候停止? 2.如果是在node-shell里运行:则递归次数不固定,但会停止!

我是文件 17927coffee 的 Shell 是 20902… 在 node 里在这附近变动 20893 20888 20886 20883v0.8.16

网上搜出来, 貌似和栈的深度有对应… 没测试 https://groups.google.com/forum/?fromgroups=#!topic/nodejs-dev/KyBoGTAq1cQ

$ node --stack_size=2048


> var err, n=0; (function f(){ n++; try {f()} catch (e) {err=e}})(), [err.message, n]
[ 'Maximum call stack size exceeded', 21809 ]
$ node --stack_size=4096


> var err, n=0; (function f(){ n++; try {f()} catch (e) {err=e}})(), [err.message, n]
[ 'Maximum call stack size exceeded', 43654 ]
$ node --stack_size=8192


> var err, n=0; (function f(){ n++; try {f()} catch (e) {err=e}})(), [err.message, n]
Segmentation fault ( oops :-)

这玩意不算真正的递归调用吧。执行一次,将内容压入任务队列中,下次循环的时候取出来执行,然后重复。并不是在一次循环中完成的。

就是说递归是不会压入队列了… 那除此之外还有区别么, 比如压入队列的环境的内容, 是跟直接递归时一样的?

这不是递归调用 你只是递归定义新函数而已

后续的 f 不是原先定义的那个 f 继续执行?

每次nexttick调用的匿名函数是新定义的,它在转调用的f,也就是你的f通过一个匿名函数调用f,中间间隔了一层,就不是递归调用了

意思能懂, 但让我自己区分还真是区分不出来了… process.nextTick 到底做了什么…

回到顶部