一个函数在setTimeout里调用自身会造成递归吗?

Humphry
  • 16.3k
    (function repaint(){
        //重绘相关代码
        setTimeout(repaint,16) ;
    })() ;

或者

    (function repaint(){
        //重绘相关代码
        requestAnimationFrame(repaint) ;
    })() ;

我在函数内部加了断点,在chrome里面查看call stack,始终是一层,这是否能证明一个函数,在setTimeout和requestAnimationFrame内调用自己不造成递归?

回复
阅读 13.3k
8 个回答
iammutex
  • 1.6k
✓ 已被采纳

答案是不会。
首先我们来理解一下什么叫递归,先不贴wiki上的说明了,大家普遍认识是,递归就是函数自身调用自身。这里面很重要的一点就是,在调用自身的过程中,父函数是没有退出的。需要等被调用的子函数退出后父函数才退出。
但是setTimeout函数不是这样执行的,它只是创建了一个定时任务,然后直接退出了,javascript的引擎线程会将这个任务放到自己的定时任务中,当定时结束后才调用子函数。
可以参考下图(来源于网络)

javascript引擎线程执行图

详细可以参考:谈谈JavaScript的异步实现

不会。因为当前函数执行完毕才会去处理超时处理函数。

这个分逻辑上和实现上。

  • 逻辑上来说,它和递归是等价的。
  • 但是实现上,它是异步执行的,不会导致栈在使用上的无限增加,因为之前的函数执行完就销毁了。

lz可以这么认为,这种异步形式的递归,不会导致栈空间的溢出。但是作为副作用,这种递归不能依靠异步调用的返回值执行操作(当然,后面可以有操作,但是无法利用到返回值)。和尾递归很像呢,至少在逻辑上两者也确实非常接近。

setTimeout是“异步”执行的(在我看来其实是伪异步,JS是单线程的,所谓的异步只是通过适当地插入到代码执行队列中延迟执行而已),其实总体相当于在一段时间内定期执行一次repaint函数而已。只不过第一次是立刻执行罢了。

可以叫循环,但不能叫递归

drinkthere
  • 1
新手上路,请多包涵

虽然称不上递归,但还是感觉repaint这个方法会被调用多次的。

closebook
  • 2
新手上路,请多包涵

每隔一段时间生产一个settimeout对象也会有开销的吧

justin
  • 2
新手上路,请多包涵

setTimeout是异步函数无可厚非,执行完当前函数之后把自身当作回调扔给定时器线程就不管了,就好比如repait();dom.onclick=function(){repait()}执行一次后,我每1s后都点击一次让它repait,这不是递归,就是函数的普通调用,下一次的函数作用域里面拿不到上一次函数作用域的变量。如果是fuction repait(a){console.log(a);setTimeout(function(){repait(++a)},1000)},是可以拿到上一次函数作用域的变量,证明还有引用不会销毁,保存在堆中,但是这不就是闭包么?函数内返回一个匿名函数给定时器线程作为回调,这个匿名函数被定时器引用,到时间这个匿名函数执行调用,调用之后创建一个新的匿名函数被引用,上一个匿名函数已经没有引用了,垃圾回收掉了。repait函数每一次都是创建一个新的匿名函数形成闭包,而有外部引用的闭包才不会被销毁。function repait(){return function (){repait()}};var niming1 = repait();var niming2 =niming1();niming1 = null;

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏