停止 jquery .then 通过用户输入链接

Stoping jquery .then chain via user input

这可能是一个简单的问题,但我完全迷路了。

我有这个功能

m.util.genericSwipeVertFunc = function (
        ajaxRequest,
        swipeOutTarget,
        swipeInTarget
) {
    var stage1, stage2, failStage, dfd = $.Deferred(), finalStage, functionPromise;

    // Swipe of screen wait for ajax request
    stage1 = function () {
        return $.when(
            ajaxRequest, // Returns $.Deferred()
            m.util.animateDeffered(swipeOutTarget, "fadeOutDown", true) // Returns $.Deferred()
        );
    };

    // Swipe and Show
    stage2 = function () {
        swipeInTarget.show();

        return m.util.animateDeffered(swipeInTarget, "fadeInDown"); // Returns $.Deferred()
    };

    finalStage = function () {
        dfd.resolve();
    }

    failStage = function () {
        console.log("fail!");
        swipeInTarget.hide();
    };

    functionPromise = stage1()
        .then(stage2)
        .then(finalStage);

    $.when(functionPromise,dfd)
        .fail(failStage);

    return dfd;
};

基本上它会做一些花哨的动画来淡入和淡出来自 ajax 函数的不同响应输出。这一切都很好,除非用户试图非常快速地在目标之间切换(在一个链完成之前他们开始另一个)我到处都是疯狂的动画。

我希望能够通过做这样的事情在任何时候拒绝链。

// called on script load.
var currentAction = $.Deferred();

// Called everytime someone starts animation chain.
currentAction.reject();
currentAction = m.util.genericSwipeVertFunc(dfd, swipeOutTarget, swipeInTarget);
            );

使用我当前的代码,failFunction 被正确命中,但它不会停止 stage2 的执行。所以它隐藏然后显示它并继续破坏东西。

那么问题来了。我如何将延迟放入链中,我可以在链执行期间随时拒绝? :)


示例fiddle http://jsfiddle.net/ff3jojbo/


更新说明

我正在为我的动画使用 animate.css。不是 jquery 动画。 我更感兴趣的是如何阻止链在用户输入的任何时候开始下一阶段。


回答fiddle http://jsfiddle.net/aefkwa8a/

尝试使用 .queue() , .promise()

// array of functions to add to queue
var arr = [];

var swipeInTarget = $("#stage1");

var swipeOutTarget = $("#stage2");

// pseudo `ajax` call
var ajaxRequest = function ajaxRequest(next) {
  return $.Deferred(function(d) {
    setTimeout(function() {
      d.resolve("ajaxRequest")
    }, Math.random() * 5000)
  }).promise()
  // Note `.then(function() {console.log(this)})` for example , 
  // can be removed
  .then(function(data) {
    console.log(data)
  }).then(next)
}

var stage1 = function stage1(next) {
  return swipeOutTarget.fadeTo(Math.random() * 5000, Math.random())
        .promise()
        // Note `.then(function() {console.log(this)})` for example , 
        // can be removed
        .then(function() {
          console.log(this)
        })
        .then(next)
}

var stage2 = function stage2(next) {
  return swipeInTarget
    .show(Math.random() * 5000, function() {
      return $(this).fadeTo(Math.random() * 2000, Math.random())
    })
    .promise()
     // Note `.then(function() {console.log(this)})` for example , 
     // can be removed
    .then(function() {
      console.log(this)
    })
    .then(next)
}
// do stuff when queue cleared
var failStage = function failStage() {
  return swipeInTarget.hide(Math.random() * 2000)
    .promise().then(function() {
      console.log("m processes stopped")
    })
}
// always do stuff when queue cleared,
// or all functions in queue complete
var finalStage = function finalStage() {
  console.log("complete", this)
}
// create jQuery object
var m = $({
  m: arr
});
// add function to `"stages"` queue
m.queue("stages", [stage1, stage2, finalStage]);
// do stuff when all functions complete , or queue cleared
m.promise("stages")
.then(finalStage);
// dequque `"stages"` queue
m.dequeue("stages");
// clear `"stages"` queue
$("button").click(function() {
  m.queue("stages", [])
  .promise("stages").always(failStage)
})
#stage2 {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>stop m processes</button>
<div id="stage1">stage1</div>
<div id="stage2">stage2</div>

OP自己的解决方案here点击几次后可能会失败。特别是,如果在某个部分飞入时单击按钮,则最新请求的部分可能会飞入,然后消失。

这个解决方案是完全不同的。

它没有使用 jQuery 的 queue/dequeue,而是使用常规的 stage1().then(stage2) 承诺链,并通过删除 CSS 动画 类 从动画元素中分离它的 animationend 处理程序,从而确保与完成相关的承诺永远不会解析。

正如您将看到的,许多功能都被分解为 jQuery 插件,这使得语法方便、紧凑。

$(function () {
    // **************************
    // *** Various outer vars ***
    // **************************
    var $sections = $('#TabSection>div').hide();
    var ajaxPromise;
    var str = {
        //various strings
        'animationend': 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
        'fadeOutClasses': 'fadeOutDown animated',
        'fadeInClasses': 'fadeInDown animated',
        'allClasses': 'fadeOutDown fadeInDown animated'
    };

    // ***********************************************
    // *** Utilities in the form of jQuery plugins ***
    // ***********************************************
    jQuery.fn.killAnim = function(animation) {
        /* jQuery plugin :
         * Remove all the animation classes from all possible targets, and
         * detach any currently attached animationend handlers.
         * Depends on: str (object).
         */
        return this.off(str.animationend).removeClass(str.allClasses);
    };
    jQuery.fn.cssAnimate = function (animation) {
        /* jQuery plugin :
         * Perform CSS animation and return promise.
         * Depends on: str (object); killAnim (plugin).
         */
        var that = this;
        return $.Deferred(function(dfd) {
            // if no target or target not visible, resolve;
            if(that.length == 0 || !that.is(':visible')) {
                dfd.resolve();
            }
            that.addClass(animation).one(str.animationend, dfd.resolve);
        }).then(function() {
            that.killAnim();
        });
    };
    jQuery.fn.genericSwipeVertFunc = function () {
        /* jQuery plugin :
         * Sequence two CSS animations - fadeOut then fadeIn.
         * Depends on: str (object); killAnim (plugin); cssAnimate (plugin).
         */
        var that = this; // swipeInTarget
        var swipeOutTarget = $sections.filter(':visible()').eq(0);
        function stage1() {
            $sections.killAnim().not(swipeOutTarget).hide();
            return swipeOutTarget.cssAnimate(str.fadeOutClasses).then(function() {
                swipeOutTarget.hide();
            });
        };
        function stage2() {
            $sections.not(that).killAnim().hide();
            return that.show().cssAnimate(str.fadeInClasses);
        };
        return stage1().then(stage2);
    };

    // **********************
    // *** Event handlers ***
    // **********************
    $('button').on('click', function (event) {
        var inTarget = $($(this).data('tar'));
        if(ajaxPromise) {
            ajaxPromise.abort('aborted');
        }
        // *** start: emulate AJAX ***
        ajaxPromise = $.Deferred(function(dfrd) {
            setTimeout(dfrd.resolve, 1000);
        });
        ajaxPromise.abort = ajaxPromise.reject;
        // *** end: emulate AJAX ***
        ajaxPromise.then(function() {
            return inTarget.genericSwipeVertFunc();
        }).fail(function(e) {
            $sections.killAnim().hide();
            console.log(e);
        });
    });
});

我相信这个解决方案更可靠。即使有很多疯狂的点击,我也无法击败它。

试一试here