【问题标题】:Javascript: settimeout recursion endless stack increase?Javascript:settimeout递归无限堆栈增加?
【发布时间】:2019-06-23 22:09:53
【问题描述】:

我的目标是使用 HTML/CSS/JS 制作背景图片的幻灯片。我发现的许多解决方案都在宣传这样的内容:

my_recursion();

function my_recursion () {
 // cycle the Background image ...
 setTimeout(my_recursion, 3000);
}

我认为这是不好的风格是错误的吗?我希望在例如循环 1000 my_recursion 的所有其他 999 个实例仍然打开/在堆栈上?这不是创建无限堆栈,消耗越来越多的内存吗?

或者是否涉及某种智能,例如“如果一个函数最后调用自己,则第 (n-1) 个函数被销毁,包括在其中分配的所有变量”?

【问题讨论】:

  • 堆栈上只有一个my_recursion 条目。第一次执行在第二次启动之前完全完成。
  • 但是为什么呢?这是否意味着在我的setTimeout 之后的var x = 1 永远不会被执行?
  • @Robert 对setTimeout() 的调用立即返回。系统会跟踪挂起的定时器,并在时间到来时调用回调函数。
  • @Robert 不,这是不可能的。我敦促您查看我链接的另一个问题,并可能更多地查看事件队列。如果您的函数在 3 秒内仍在运行,那么 不会运行其他任何东西。只有当它完成时,任何其他计划的代码才会运行 - 你不会得到两个并行执行。

标签: javascript recursion slideshow


【解决方案1】:

这不会导致无休止的堆栈增加,因为 setTimeout 的工作方式,而且恕我直言,这是不错的风格。

setTimeout 不保证代码将在给定超时后直接运行。相反,在该超时之后,它会将回调推送到“队列”,当堆栈为空时将处理该队列。所以它只会在 my_recursion 返回并且堆栈为空时运行。

如果一个函数在最后调用自己(...)

my_recursion 不会在任何地方调用自己。它只是将自己作为参数传递给setTimeout。之后,它会继续执行,之后直接返回,并从堆栈中弹出。

This presentation explains the stack and the event queue.

【讨论】:

  • 这是否意味着,如果我在setTimeout 之后有代码执行时间超过 3 秒(例如 4 秒),那么下一个函数将开始并行执行,而我的 (n-1 )th 实例仍在运行?或者这是否意味着下一个实例在 4 秒后启动,因为它在 3 秒后无法启动,因为第 (n-1) 个函数仍在运行?
  • 我不认为函数需要立即显式调用自身以使其递归。只要它导致同一函数的另一次执行,它就应该是递归的。如果只需要直接和显式调用,那么在考虑函数是否递归时,尾调用优化 (TCO) 将很重要。
  • @Robert:不,代码不会并行执行。 setTimeout 不保证代码将在给定超时后直接运行。相反,在该超时之后,它会将回调推送到“队列”,当堆栈为空时将处理该队列。所以它只会在my_recursion 返回并且堆栈为空时运行。
  • 我在 JSConf 的演示文稿中添加了一个链接来解释这一点。
  • @vlaz:你是对的,我已经更新了我的答案以更准确。
【解决方案2】:

在您的问题中,您的函数没有任何参数。在实际的实现中,我希望你打算使用它们。

const cycleBackground = (elem, bgs = [], ms = 1e3, i = 0) =>
  ( elem.setAttribute ('style', bgs[i])
  , setTimeout
      ( cycleBackground      // function to schedule
      , ms                   // when to schedule, ms from now
      , elem                 // user-specified element to change
      , bgs                  // user-specified backgrounds
      , ms                   // user-specified delay
      , (i + 1) % bgs.length // next background index
      )
  )

const backgrounds =
  [ "background-color: red;"
  , "background-image: linear-gradient(45deg, cyan 0%, purple 75%);"
  , "background-color: green;"
  ]

// call site
cycleBackground
  ( document.body // element to target
  , backgrounds   // list of backgrounds
  , 3e3           // delay, 3 seconds
  )
p {
  text-align: center;
  font-size: 3vw;
  font-weight: bold;
  color: white;
}
<p>Wait 3 seconds...</p>

【讨论】:

    【解决方案3】:

    代码很好。它会破坏所有变量,因为当您第一次调用它时。它setTimeout() 用于下一个function 并最终返回。你的功能不return下一个。

    my_recursion();
    
    function my_recursion () {
     // cycle the Background image ...
     setTimeout(my_recursion, 3000); //Sets timeout for next function.
     //returns undefined here
    } 
    

    【讨论】:

      【解决方案4】:

      添加到https://stackoverflow.com/a/54443904/11022136。 想提供一些证据。 在节点 14 上运行以下命令。

      test.js:

      let i = 10;
      const canThisOverflow = () => {
          i--;
          console.trace();
          if (i > 0) setTimeout(canThisOverflow, 1);
      }
      canThisOverflow();
      

      输出:堆栈大小没有增加

      Trace
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test.js:4:10)
          at Object.<anonymous> (/Users/arjunmalik/Shipsy/query-builder/test.js:7:1)
          at Module._compile (internal/modules/cjs/loader.js:1063:30)
          at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
          at Module.load (internal/modules/cjs/loader.js:928:32)
          at Function.Module._load (internal/modules/cjs/loader.js:769:14)
          at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
          at internal/main/run_main_module.js:17:47
      Trace
          at Timeout.canThisOverflow [as _onTimeout] (/Users/arjunmalik/Shipsy/query-builder/test.js:4:10)
          at listOnTimeout (internal/timers.js:554:17)
          at processTimers (internal/timers.js:497:7)
      Trace
          at Timeout.canThisOverflow [as _onTimeout] (/Users/arjunmalik/Shipsy/query-builder/test.js:4:10)
          at listOnTimeout (internal/timers.js:554:17)
          at processTimers (internal/timers.js:497:7)
      

      test2.js:

      let i = 10;
      const canThisOverflow = () => {
          i--;
          console.trace();
          if (i > 0) canThisOverflow();
      }
      canThisOverflow();
      

      输出:堆栈大小增加

      Trace
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:4:10)
          at Object.<anonymous> (/Users/arjunmalik/Shipsy/query-builder/test2.js:7:1)
          at Module._compile (internal/modules/cjs/loader.js:1063:30)
          at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
          at Module.load (internal/modules/cjs/loader.js:928:32)
          at Function.Module._load (internal/modules/cjs/loader.js:769:14)
          at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
          at internal/main/run_main_module.js:17:47
      Trace
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:4:10)
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:5:13)
          at Object.<anonymous> (/Users/arjunmalik/Shipsy/query-builder/test2.js:7:1)
          at Module._compile (internal/modules/cjs/loader.js:1063:30)
          at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
          at Module.load (internal/modules/cjs/loader.js:928:32)
          at Function.Module._load (internal/modules/cjs/loader.js:769:14)
          at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
          at internal/main/run_main_module.js:17:47
      Trace
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:4:10)
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:5:13)
          at canThisOverflow (/Users/arjunmalik/Shipsy/query-builder/test2.js:5:13)
          at Object.<anonymous> (/Users/arjunmalik/Shipsy/query-builder/test2.js:7:1)
          at Module._compile (internal/modules/cjs/loader.js:1063:30)
          at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
          at Module.load (internal/modules/cjs/loader.js:928:32)
          at Function.Module._load (internal/modules/cjs/loader.js:769:14)
          at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
          at internal/main/run_main_module.js:17:47
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-23
        • 2020-07-20
        • 2017-09-05
        • 1970-01-01
        • 2018-09-24
        • 2019-10-13
        • 2018-10-16
        • 1970-01-01
        相关资源
        最近更新 更多