如何减少javascript / jquery中的嵌套回调

时间:2017-04-27 17:24:01

标签: javascript jquery

我的任务是重写这段可怕的代码,这些代码意味着在网页上依次淡化地图上的图层(它们都是透明的png)。它需要按顺序操作,然后循环回到没有可见层的开始,并逐个淡入。这个序列应该永远重复。

我不确定最常推荐的方法是在javascript中做什么,并对堆栈溢出有什么兴趣。

必须有比这更好的方法!我对任何方法的优点/缺点感兴趣。

setInterval(function(){
    $("#layer-1").fadeIn(1000, function() {
      $("#layer-2").fadeIn(1000, function() {
        $("#layer-3").fadeIn(1000, function() {
          $("#layer-4").fadeIn(1000, function() {
            $("#layer-5").fadeIn(1000, function() {
              $("#layer-6").fadeIn(1000, function() {
                $("#layer-7").fadeIn(1000, function() {
                  $("#layer-8").fadeIn(1000, function() {
                    // pause for 2 seconds, then reset and hide all layers:
                    $("#home-map .layer").delay(2000).fadeOut();
                  });
                });
              });
            });
          });
        });
      });
    });
  }, 10000)

编辑:我认为这与其他答案不同的原因是因为我试图在无限循环中设置事物,以及链接动画。在javascript中有很多解决回调地狱的方法,这是一个非常常见的问题,所以毫无疑问会有类似的问题。

5 个答案:

答案 0 :(得分:6)

使用类而不是id,然后遍历它们并根据索引添加延迟

var layers = $(".layer").length;

function foreverLoop() {

  $(".masterLayer").show();
  $(".layer").hide();

  $(".layer").each(function(index) {
    $(this).delay(1000*index).fadeIn(1000);
  });
  
  $(".masterLayer").delay(1000*layers + 2000).fadeOut(1000);
  
  setTimeout("foreverLoop()", 1000*layers + 3000 + 500);
}

foreverLoop();
.layer {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="masterLayer">
  <div class="layer">layer 1</div>
  <div class="layer">layer 2</div>
  <div class="layer">layer 3</div>
  <div class="layer">layer 4</div>
  <div class="layer">layer 5</div>
  <div class="layer">layer 6</div>
</div>

答案 1 :(得分:5)

接受挑战......使用递归方法。

(function main(index){
   if(index >= 9){
     return $.when($("#home-map .layer").delay(2000).fadeOut()).then(function(){
         main(1); //restart
     });
   }
  $("#layer-"+index).fadeIn(1000, function(){
         main(index+1);
  });
 })(1);

http://jsbin.com/rurokipipi/1/edit?output

答案 2 :(得分:5)

使用async await + promises

如果您在动画后致电.promise(),您将收到回复。多亏了它,你可以等待它完成

async function animate() {
    await $("#layer-1").fadeIn(1000).promise()
    await $("#layer-2").fadeIn(1000).promise()
    await $("#layer-3").fadeIn(1000).promise()
    await $("#layer-4").fadeIn(1000).promise()
    await $("#layer-5").fadeIn(1000).promise()
    await $("#layer-6").fadeIn(1000).promise()
    await $("#layer-7").fadeIn(1000).promise()
    await $("#layer-8").fadeIn(1000).promise()
    // pause for 2 seconds, then reset and hide all layers:
    await $("#home-map .layer").delay(2000).fadeOut().promise();
}

const loop = () => animate().then(loop)
loop()

看着这个就能很好地适应for循环。

async function animate() {
    for (let i = 1; i < 9; i++)
        await $(`#layer-${i}`).fadeIn(1000).promise()

    // pause for 2 seconds, then reset and hide all layers:
    await $("#home-map .layer").delay(2000).fadeOut();
}

这仅适用于最近的浏览器

如果你做的话,猜测它可以变得更有活力

for (let layer of $("#home-map .layer"))
    await $(layer).fadeIn(1000).promise()

这是针对相同问题的es5替代方案,但使用reduce to chain promises

&#13;
&#13;
function animate() {
  $(".layer").toArray().reduce(function(prev, elm){
    return prev.then(function(){
      return $(elm).fadeIn(1000).promise();
    })
  }, Promise.resolve()).then(function(){
    // do the master
    $(".masterLayer").delay(2000).fadeOut();
  })
}

animate()
&#13;
.layer {
  display: none;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="masterLayer">
  <div class="layer">layer 1</div>
  <div class="layer">layer 2</div>
  <div class="layer">layer 3</div>
  <div class="layer">layer 4</div>
  <div class="layer">layer 5</div>
  <div class="layer">layer 6</div>
</div>
&#13;
&#13;
&#13;

答案 3 :(得分:3)

var MAX_LAYER_NUMBER        = 8;
var LAYER_FADE_IN_DURATION  = 1000;
var LAYER_FADE_OUT_DURATION = 1000;
var LAYER_FADE_IN_OUT_DELAY = 2000;

function fadeInLayer(layerNumber, done) {
  if (layerNumber <= MAX_LAYER_NUMBER) {
    $('#layer-' + layerNumber).stop(true, true).fadeIn(LAYER_FADE_IN_DURATION, function() {
      done(layerNumber);
      fadeInLayer(layerNumber + 1, done);
    });
  }
}

function fadeInOutLayers() {
  fadeInLayer(1, function(layerNumber) {
    if (layerNumber === MAX_LAYER_NUMBER) {
      $(".layer").delay(LAYER_FADE_IN_OUT_DELAY).fadeOut(LAYER_FADE_OUT_DURATION);
    }
  });
}


$(function() {
  fadeInOutLayers();
  setInterval(fadeInOutLayers, LAYER_FADE_IN_DURATION * MAX_LAYER_NUMBER + LAYER_FADE_IN_OUT_DELAY + LAYER_FADE_OUT_DURATION)
});
.layer {
  display: none;
}
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div id="layer-1" class="layer">layer-1</div>
<div id="layer-2" class="layer">layer-2</div>
<div id="layer-3" class="layer">layer-3</div>
<div id="layer-4" class="layer">layer-4</div>
<div id="layer-5" class="layer">layer-5</div>
<div id="layer-6" class="layer">layer-6</div>
<div id="layer-7" class="layer">layer-7</div>
<div id="layer-8" class="layer">layer-8</div>

答案 4 :(得分:1)

您所拥有的是一系列反复出现的异步函数调用。你可以使用对flatten your arrow code的承诺,但我们不是在谈论我们希望抛出错误的代码,因此不需要异步try..catch,这是承诺的

按顺序发生的异步函数是队列。 jQuery有一个很好的queue method,这就是我在这里推荐的。

现在,因为您正在各种不同的元素上执行此队列,所以您需要选择一个公共元素来存储核心队列。在这种情况下,我将使用body,但您可以使用几乎任何元素(我建议您使用动画的所有元素的最近共同父项,因为它将允许您使用页面上的结构在没有队列干扰彼此的多个地方,但这更像是一个高级步骤。)

fx队列是默认队列,这是动画实际发生的位置。我们希望与fx队列分开管理此队列,以便其他动画可以与此队列一起发生。

function myAnimation() {
  $('body')
    // queue up the next step of the animation
    .queue('my-animation', (next) => {
      // `next` is a function that tells the queue to continue
      // on to the next step. We pass next to the complete
      // callback of the animation so that they can continue
      // fluidly.
      $('#player-1').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-2').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-3').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-3').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-4').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-5').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-6').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-7').fadeIn(1000, next);
    })
    .queue('my-animation', (next) => {
      $('#player-8').fadeIn(1000, next);
    })
    // here we want to wait for a bit before continuing with
    // the rest of the queue
    .delay(2000, 'my-animation')
    .queue('my-animation', (next) => {
      $('#home-map .layer').fadeOut(next);
    })
    // here we repeat the animation so that a `setInterval` call
    // is unnecessary, and so that we don't have to care how long
    // the animation takes in total
    .queue('my-animation', (next) => {
      // queue up the next iteration of the animation
      myAnimation();
      // continue with the queued animation
      next();
    });
}

myAnimation(); // queue up the animation
$('body').dequeue('my-animation'); // start the animation

现在,这段代码非常重复。我会将它作为练习留给读者,将其改为简单的for循环或任何你想要的循环。这只是整体简化的一个例子。

此示例中的代码比原始代码长,但不要让它欺骗你。因为它是一个队列,所以稍后更容易推理和编辑。可以插入和删除步骤,而无需更改等待时间或尝试重新平衡嵌套的括号/花括号。