【问题标题】:Weird infinite loop bug in jquery recursive functionjquery递归函数中奇怪的无限循环错误
【发布时间】:2011-06-30 00:48:40
【问题描述】:

我正在处理的项目中有一个奇怪的问题。这会自动更改图像源和 div 的内容。

我编写了一个函数,但它陷入了无限循环并且页面无法加载(页面始终显示加载页面)。

这些是代码:

$.fn.extend({
    changehaber: function(a){
     $('#cokokunanlarcontent').fadeOut('slow',function() {
           $('.jquerycokokunanlarbutton').attr('src','images/sabah/buton-pasif.png');
           $('img[rel="'+a+'"]').attr('src','images/sabah/buton-aktif.png'); 
        }).html($('#'+a).html()).fadeIn('slow');
        return this;
    }
});

function slidecokokunanlar() {
    $('#cokokunanlarcontent').html($('#cokokunanlar1').html()).delay(3000).changehaber('cokokunanlar2').delay(3000).changehaber('cokokunanlar3').delay(3000).changehaber('cokokunanlar4').delay(3000).changehaber('cokokunanlar5').delay(3000);
    slidecokokunanlar();
}

slidecokokunanlar();

这里有什么问题,执行此操作时,我希望该功能无限工作,但页面显示它始终在加载。这是控制台的输出:

Uncaught RangeError: Maximum call stack size exceeded

提前致谢

【问题讨论】:

    标签: javascript jquery loops


    【解决方案1】:

    你不能在不阻塞整个执行堆栈的情况下从内部调用函数。通过从内部调用函数,您可以有效地阻止它返回,并且由于 Javascript 是单线程的,所以一切都会停止!

    把你的函数改成这样:

    function slidecokokunanlar() {
        $('#cokokunanlarcontent').html($('#cokokunanlar1').html()).delay(3000)...
        setTimeout(slidecokokunanlar, 0);
    }
    

    这允许在不阻塞 UI 的情况下并发执行,从而使您的页面保持响应。

    有关其工作原理的更多信息,请参阅this article on "chunking"

    【讨论】:

    • 你的“setTimeout”参数倒过来了,函数名不应该用引号引起来。
    • 它可能会消除错误,但您仍然会以极快的速度递归调用该函数。
    • 即使我使用了setTimeout,我也经历过同样的事情;似乎这是最好的选择。我想知道setInterval 在不同的上下文中是否会有所作为?
    【解决方案2】:

    这是因为JavaScript doesn't have proper tail calls

    你的函数永远在自己结束时调用自己。第一个永远不会完成并返回,第二个也不会,直到你用完堆栈并爆炸为止。

    您可以尝试改用setTimeout。请参阅example on jsFiddle

    编辑您可能不想使用 0,除非您确实需要它连续运行。即使使用 100,您也会每秒执行 10 次函数。

    function foo(){
      console.log('foo');
      setTimeout(foo, 0);  
    }
    
    foo();
    

    【讨论】:

      【解决方案3】:

      这是一种更清洁的方法。

      var coko = $('#cokokunanlarcontent');             // cokokunanlarcontent
      var cokos = $('[id^="cokokunanlar"]').not(coko);  // cokokunanlar1..2..3 etc
      
      var total = cokos.length;  // total quantity
      
      var i = 0;
      
      var allow = true;
      
      $('.jquerycokokunanlarbutton').attr('src','images/sabah/buton-pasif.png');
      
      function slidecokokunanlar( isRestart ) {
      
          if( !isRestart ) {
              $('img[rel="' + cokos[i].id + '"]').attr('src','images/sabah/buton-aktif.png');
      
              coko.html( cokos.eq(i).html() )
                  .fadeIn( 'slow' );
          }
          if( allow ) {
              coko.delay( 3000 )
                  .fadeOut('slow', function() {
                     i = (++i % total);
                     slidecokokunanlar();  // recursively call with next index or 0
                  });
          }
      }
      
      slidecokokunanlar();  // start it off
      
      function restartSlider() { 
          allow = true;
          slidecokokunanlar( true );
      }
      
      function stopSlider() { 
          allow = false;
      }
      
      stopSlider();   // to stop it
      
      restartSlider();  // to restart it
      

      【讨论】:

      • 谢谢,我也想过这个问题,但是我正在更改的图像将不得不停止功能(动画的更改),我正在通过 (clearTimeout(variable) ),哪个变量是“var variable = setTimeout(functionname,0);”而且我无法使用类似的方法来管理它。你的方法怎么可能?
      • @Arda:每次运行递归函数调用时,只需检查一个标志,例如var allow = true;。该变量当然需要在slidecokounalar() 函数之外。如果要停止递归,请将标志设置为false。要重新启动它,请将标志设置为true,然后调用slidecokounalar()。如果您希望它从中断处继续,请将i 放在函数外部的变量中,并每次更新它,而不是将其作为参数传递。我将更新我的答案以实施这些想法。
      • 已更新。我给slidecokokunanlar(); 提供了一个特殊的isRestart 参数,这样如果它正在重新启动(而不是第一次启动),它就不会尝试加载fadeIn 已经显示的内容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-18
      • 1970-01-01
      相关资源
      最近更新 更多