【问题标题】:Can I have a setTimeout inside a handler of another setTimeout?我可以在另一个 setTimeout 的处理程序中有一个 setTimeout 吗?
【发布时间】:2012-12-02 22:57:36
【问题描述】:

这是一个快速(坏掉的)jsfiddle:http://jsfiddle.net/wH2qF/

由于某种原因这不起作用...是因为我在另一个 setTimeout 的处理程序中有一个 setTimeout 吗?

$(function() {
   $("#Volume").click(function() {
      setTimeout(triggerVolumeChange, 4000);
      function triggerVolumeChange() 
      {
          var volumeDiv = document.getElementById("volumeNumber");
          var volumeOld = 8;
          var volumeNew = 37;
          var timeNew = (1000/(volumeNew-volumeOld));
          changeVolume();
                        
          function changeVolume()
          {
             volumeDiv.innerHTML = volumeOld;
             volumeOld++;
             if (volumeOld <= volumeNew) setTimeout(changeVolume, timeNew);
          };
      });
});

应该指定,为了清楚起见,我从该 Click 函数中删除了其他内容,并且还澄清了哪些不完全有效,好吧,基本上,我点击并没有任何反应,而如果我删掉这段代码它可以很好...实际上 vars 的设置也可以正常工作(我自然认为)但是当我粘贴或取消注释 changeVolume() 函数时,点击再次停止工作...有什么想法吗?

--

另一个澄清:我想要做的是,在点击时,在一个字符串中模拟从 8 到 37 的音量。因此该函数内部的 setTimeout。

--

根据你的要求,这是整个代码...我怀疑它是否有意义,但这里是...仅供参考,点击这将触发一些动画来模拟应用程序的流程我'我在设计..

<script>
            $(function() {
                $("#Volume").click(function() {
                                        
                    var userPrompt = document.getElementById("userPrompt")
                    userPrompt.innerHTML = "Change volume to 37";
                                        
                    var avatarIcon = document.getElementById("avatarIcon");
                    avatarIcon.innerHTML = "<img src='imgs/haloIcons-volume_82x76.png' alt='Volume'/>";
                
                    var hints = document.getElementById("hints");
                    hints.style.opacity = 0;
                    $(".dragonTvOut").toggleClass("dragonTvIn");
                    
                    setTimeout(triggerP, 1000);
                    function triggerP()
                    {
                        var halo = document.getElementById('avatar');
                        if( 'process' in halo ) { 
                            halo.process();
                        };
                    };
                    
                    setTimeout(triggerUserPrompt, 2000);
                    function triggerUserPrompt() 
                    {
                        document.getElementById("userPrompt").className = "userPromptIn";
                    };
                    
                    setTimeout(triggerVolumeChange, 4000);
                    function triggerVolumeChange() 
                    {
                        document.getElementById("userPrompt").className = "userPromptEnd";
                        
                        var halo = document.getElementById('avatar');
                        if( 'resume' in halo ) { 
                            halo.resume();
                        }

                        document.getElementById("avatarIcon").className = "avatarIconEnd";
                        
                        var volumeDiv = document.getElementById("volumeNumber");
                        var volumeOld = 8;
                        var volumeNew = 37;
                        var timeNew = (1000/(volumeNew-volumeOld));
                        changeVolume();
                        
                        function changeVolume()
                        {
                            volumeDiv.innerHTML = volumeOld;
                            volumeOld++;
                            if (volumeOld <= volumeNew) setTimeout(changeVolume, timeNew);
                        }​;
                        
                        var side = 100;
                        var paper = new Raphael(volumeArcAnim, 100, 300);
                        
                        paper.customAttributes.arc = function (xloc, yloc, value, total, R) {

                            var alpha = 360 / total * value,
                                a = (90 - alpha) * Math.PI / 180,
                                x = xloc + R * Math.cos(a),
                                y = yloc - R * Math.sin(a),
                                path;
                            if (total == value) {
                                path = [
                                    ["M", xloc, yloc - R],
                                    ["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
                                ];
                            } else {
                                path = [
                                    ["M", xloc, yloc - R],
                                    ["A", R, R, 0, +(alpha > 180), 1, x, y]
                                ];
                            }
                            return {
                                path: path
                            };
                        };
                    
                        var arcWidth = 87;
                        var strokeRadius = arcWidth/2;
                        
                        var indicatorArc = paper.path().attr({
                            "stroke": "#ffffff",
                            "stroke-width": 3,
                            arc: [side/2, side/2, 12, 100, strokeRadius]
                        });
                        
                        indicatorArc.animate({
                            arc: [side/2, side/2, 60, 100, strokeRadius]
                        }, 1500, "<>", function(){
                            // anim complete here
                        });
                        
                    };
                                            
                });
            });
            </script>

【问题讨论】:

  • 您遇到了什么错误?我认为setTimeout() 需要一个字符串来进行方法调用。
  • @Knownasilya - 它需要一个字符串或一个函数。
  • 您可能需要考虑重构您的函数,使它们相互传递参数,而不是依赖于在父函数的范围内执行。
  • @Knownasilya 有几个原因:它必须 eval 你的字符串,这由于其自身原因而被认为是一种不好的做法。此外,它会强制您的“代码”(字符串)在全局上下文中执行,在诸如 OPs 代码之类的示例中,这会破坏代码(triggerVolumeChangechangeVolume 都不会在全局上下文中定义)

标签: javascript settimeout


【解决方案1】:

是的,你可以在另一个里面有一个setTimeout()——这是用于重复定时事件的典型机制。

你的不工作的原因与setTimeout() 本身无关;这与您嵌套函数的方式有关。

changeVolume() 函数在 triggerVolumeChange() 中意味着你不能直接使用它的名字来引用它。

对您来说最快的解决方案是删除嵌套,使 changeVolume() 位于根级别,而不是嵌套在 triggerVolumeChange() 内。

【讨论】:

  • 您关于它实际上是triggerVolumeChange.changeVolume 的评论不正确。确实,changeVolume 只能在triggerVolumeChange 内部访问,所以这很可能是这里的问题。如果您修复/澄清了该部分,我会对此表示赞成
  • @JuanMendes - 当然,我会修改它。我知道不能这样称呼它(因此这不是我的建议);我试图指出它是如何以编写方式而不是如何调用来适应命名空间的。
  • 关于如何“转换”该函数以在 triggerVolumeChange() 的根目录中工作的任何想法
  • 嗯...当将changeVolume()移出triggerVolumeChange()也会使changeVolume()中使用的所有变量都未定义,因为它们将超出范围...
  • 嗯...实际上,changeVolume 没有在 triggerVolumeChange 之外引用?
【解决方案2】:

您缺少}

$(function() {
   $("#Volume").click(function() {
      setTimeout(triggerVolumeChange, 4000);
      function triggerVolumeChange()
      {
          var volumeDiv = document.getElementById("volumeNumber");
          var volumeOld = 8;
          var volumeNew = 37;
          var timeNew = (1000/(volumeNew-volumeOld));
          changeVolume();

          function changeVolume()
          {
             volumeDiv.innerHTML = volumeOld;
             volumeOld++;
                 if (volumeOld <= volumeNew) setTimeout(changeVolume, timeNew);
          };
      } // that one was missing
   });
});

【讨论】:

  • 这很可能是因为我从我的代码中复制。我的代码中没有遗漏任何 }...
【解决方案3】:

在您损坏的示例http://jsfiddle.net/wH2qF/ 中存在一些问题

  • 你忘了告诉 jsfiddle 使用 jQuery
  • 卷跨度的 id(在 JS 中)是 ph,但应该是 volumeNumber(在 HTML 中)

Click here to see a working version

如果你从 jsfiddle 的库中选择了 jQuery,你会看到一个错误

未捕获的类型错误:无法将属性“innerHTML”设置为 null

这让我相信你的 jsfiddle 不能很好地代表你的问题。也许尝试创建另一个减少,因为您添加的那个只有“愚蠢”的错误?

【讨论】:

    【解决方案4】:

    如果您不想使用setInterval(),可以使代码与这些更改一起工作:

    $(function() {
        $("#Volume").click(function() {
            setTimeout(triggerVolumeChange, 4000);
            function triggerVolumeChange () {
                var volumeDiv = document.getElementById("volumeNumber");
                var volumeOld = 8;
                var volumeNew = 37;
                var timeNew = (1000/(volumeNew-volumeOld));
                var changeVolume = function () {
                    volumeDiv.innerHTML = volumeOld;
                    volumeOld++;
                    if (volumeOld <= volumeNew) setTimeout(changeVolume, timeNew);
                };
                changeVolume();
            }
        });
    });
    

    jsFiddle 的工作演示。

    【讨论】:

    • 我已将您的代码复制到 jsfiddle 中,但它无法正常工作...jsfiddle.net/wH2qF/2 有什么想法吗?
    • @ttothec 我不知道......我在发布之前在本地测试了代码,它运行良好:(。
    • @ttothec 好的,它现在可以工作了,只需从小提琴的框架面板中选择jQuery。感谢 Juan Mendez 的提示:)。
    【解决方案5】:

    从技术上讲,计时器的启动位置没有区别。在大多数情况下,它被实现为带有标识符和相关回调处理程序的计时器列表。

    所以如果你的逻辑是正确的就可以了。没有不可预知的条件带来无限的调用序列,也没有无限量的超时实例等等。

    例如模仿setInterval函数:

    // Bad (unexpected multiple sequences started per each event)
    
    const handler = () => {
      setTimeout(handler)
    }
    
    <Button onClick={handler} />
    
    // Good (previous interval closed before start new one)
    
    const [id, idUpdate] = useState(-1)
    
    const handler = () => {
      const id = setTimeout(handler)
      idUpdate(id)
    }
    
    const start = () => {
      if(id !===-1) clearTimeout(id)
      idUpdate(-1)
      handler()
    }
    
    <Button onClick={start} />
    

    或者其他的

    // Bad (infinite events with small time will take all cpu time)
    
    const handler = () => {
      setTimeout(handler, 0) // actually mean several ms
    }
    
    // Good (special condition for finish iterations)
    
    let count = 20
    
    const handler = () => {
      if(!count) return
      count--
      setTimeout(handler, 0)
    }
    

    【讨论】:

      最近更新 更多