【问题标题】:control flow and promises控制流和承诺
【发布时间】:2015-06-06 19:02:14
【问题描述】:

我在 html 中有很多 div。每个 div 包含动画或一些 js 代码,这些代码仅在此 div 可见时调用:

<button id='start' class='start'>Start</button> 

<div class="step1 later">
  <div class='red later'>Show first</div>
  <div class='aqua later'>Show later</div>
</div>

<div class="step2 later">
  <div class='green later'>Show together</div>
  <div class='yellow later'>Show together</div>
</div>

我的目标是显示divstep1 类并调用一些js,然后显示step2 等等。它看起来像滑块。对于这个例子,我想显示step1,然后显示divred 类,延迟后我想显示aqua。接下来,我们转到step2 并立即显示两个div:yellowgreen。我决定为此使用 Promise (Q.js)。这是我的代码:

$(function () {
  var currentStep = 1,
      handlers = {};  

  $('#start').click(function() {
    for(var i = 0; i < 2; i++) {          
      showStep(currentStep + i);
    }     
  });      

  function showStep(step) {
    var name = 'step' + step;
    $('.' + name).show();

    handlers[name]();
  }


  handlers.step1 = function() {
    Q().then(function() {
       $('.notification').html('Step1 started');
    })
    .delay(2000)
    .then(function() {
       showElem('red');     
    })
    .delay(2000)
    .then(function() {
       showElem('aqua');     
    });
  };

    handlers.step2 = function() {
    Q().then(function() {
       $('.notification').append('Step2 started');
    })
    .delay(2000)
    .then(function() {
       showElem('green'); 
       showElem('yellow'); 
    });    
  };

  function showElem(classSelector) {
    $('.' + classSelector).fadeIn(100);
  }

});

所有动画和延迟作品都发现除了一件事。我的代码不会等待一步完成,处理程序几乎同时启动。
演示:JSBIN

【问题讨论】:

  • 你的意思是像this

标签: javascript promise q


【解决方案1】:

使用 Q 来安排 jQuery 动作是相当不寻常的。通过明智地使用其.delay().promise() 方法,jQuery 能够可靠地完成这项工作 - 仅靠它自己。

在下面的代码重写中:

  • promise 从各种函数返回以允许链接
  • 主(启动)例程是一个简单的两步 .then()
  • showElem()showStep() 是不必要的并且消失了
  • handlers 被写为对象字面量。
jQuery(function ($) {
    var handlers = {
        'step1': function() {
            $('.notification').html('<div>Step1 started</div>');
            return $(".step1").show(0).delay(2000).promise().then(function() {
                return $('.red').fadeIn(100).delay(2000).promise();
            }).then(function() {
                return $('.aqua').fadeIn(100).promise();
            });
        },
        'step2': function() {
            $('.notification').append('<div>Step2 started</div>');
            return $(".step2").show(0).delay(2000).promise().then(function() {
                return $('.green, .yellow').fadeIn(100);
            });
        }
    };

    $('#start').click(function() {
        handlers.step1().then(handlers.step2);
    });
});

DEMO

编辑

对于大量的处理程序,您可以将“启动”例程按如下方式进行速写:

$('#start').click(function() {
    handlers.step1()
    .then(handlers.step2)
    .then(handlers.step3)
    .then(handlers.step4)
    .then(handlers.step5)
    .then(handlers.step6)
    ...
    ;
});

但是,这可能需要大量输入,并且无法满足可变数量的处理程序。

解决方案相当简单。

首先,将handlers 写为数组,而不是对象。

var handlers = [
    //step 0
    function() {
        $('.notification').html('<div>Step1 started</div>');
        return $(".step1").show(0).delay(2000).promise().then(function() {
            return $('.red').fadeIn(100).delay(2000).promise();
        }).then(function() {
            return $('.aqua').fadeIn(100).promise();
        });
    },
    //step 1
    function() {
        $('.notification').append('<div>Step2 started</div>');
        return $(".step2").show(0).delay(2000).promise().then(function() {
            return $('.green, .yellow').fadeIn(100);
        });
    },
    //step 2,
    ...
    //step 3,
    ...
];

现在,您可以使用数组方法.reduce() 来扫描数组,构建一个承诺链:

$('#start').click(function() {
    handlers.reduce(function(promise, handler) {
        return promise.then(handler);
    }, $.when());
});

这里,$.when() 是一个已解决的“种子”承诺,它可以启动链。

DEMO

【讨论】:

  • 我使用 jQuery 只是一个例子。在实际项目中我不想使用 jQuery。我想将velocity.jsmove.js 用于动画和其他用于DOM 操作/选择器引擎。我想我必须写一下。
  • 另外,在实际项目中,大概会有20-50个handlers。
  • 我不知道velocity.jsmove.js。如果他们的动画不支持 jQuery 之类的 Promise,您可能会考虑在低级别对它们进行 Promise,以便在更高级别提供可链接性。
  • 您的 20-50 个处理程序可能对他们有重复的模式。如果是这样,那么您也许可以编写较少数量的 generic 处理程序,这些处理程序可以通过传递参数来控制。
  • 感谢您的精彩回答。
【解决方案2】:

问题在于您最初的“for”循环,您可以在其中一次执行所有“showStep”,而无需等待承诺。

你可以这样做:

$('#start').click(function() {
  showStep(1);
});

function showStep(step) {
  var name = 'step' + step;
  $('.' + name).show();
  handlers[name]().then(function() {
    if (step < 2) {
      showStep(step+1);
    }
  });
}

并让您的处理程序返回承诺。

【讨论】:

  • 如果showStep() 需要与其他东西联系起来,return 的承诺通常是很好的形式。您还需要在内部return showStep(step+1)
猜你喜欢
  • 2017-12-14
  • 2016-05-07
  • 2015-09-29
  • 2014-06-04
  • 1970-01-01
  • 2017-05-12
  • 2013-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多