制作一个jquery函数,等待它之前的调用已经解决

时间:2014-11-11 06:55:23

标签: javascript jquery function

所以,我已经搜索了这个高低,也许我只是在理解jQuery的延迟功能时遇到了问题,或者我完全走错了路。所以任何帮助都会受到赞赏!

我基本上有一个自定义jQuery函数messager,它会显示一条带有fadeOutfadeIn的邮件。

(function ( $ ) {

    $.fn.messager = function(message, effect, speed) {

        $(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed);

        return this;
    };

}( jQuery ));

所以,我有一个名为$elem的div,当$elem.messager被多次调用(使用不同的消息)时,我希望messager函数等到最后一次调用完成。就像上一个FadeIn已经完成一样。因为目前发生的是该函数的第二次调用是覆盖函数第一次调用的动画效果。

有什么想法吗?

4 个答案:

答案 0 :(得分:1)

jQuery Deferred Way

jQuery延迟对象(大致妥协CommonJS Promises API)可以帮助我们管理排队操作。这是我对排队消息的实现。您可以在一次调用中将多个消息作为数组传递,或者轻松地同步不同的消息板,因为#messager()返回jQuery对象本身,但也包装为promise对象,只有在显示消息时才会解析。



(function ($) {
    function awaits($el) {
       var awaits = $el.data('_awaits');
       awaits || $el.data('_awaits', awaits = []);
       return awaits;
    }
  
    function resolveNext(curr /*, ignored */) {
       var head = awaits(this).shift();
       if (head === curr) {
           resolveNext.call(this, 'not await');
       } else {
           head && head.resolve();
       }
    }
  
    function display(message, speed) {
        var $self = this, await = $.Deferred(), willDone = $.Deferred();
        awaits($self).push(await) > 1 || await.resolve();
        
        await.done(function() {
            function reveal() {
               $self.text(message).fadeIn(speed, function() {
                   resolveNext.call($self, await);
                   willDone.resolve();
               });
            }

            $self.fadeOut(speed/2, reveal);
        });
        return willDone.promise(this);
    };

    $.fn.messager = function(message, speed) {
        speed = speed || 500;

        if ($.isArray(message)) {
            var arr = [];
            message.forEach(function(m) {
                arr.push(display.call(this, m, speed));
            }, this);
            return $.when.apply(this, arr);
        } else {
            return display.call(this, message, speed);
        }
    }

}( jQuery ));



function play() {
  $('#msgbox1').messager(['A demo of', 'queued messages'], 1000);
  for (var i = 3; i > 0; i--) $('#msgbox1').messager(i);
  $('#msgbox1').messager(['Ready to sing...', 'Singing...']);    
  
  for (var i = 8; i > 0; i--) $('#msgbox2').messager('***');    
  for (i = 1; i < 8; i++) $('#msgbox2').messager(String.fromCharCode(64 + i));
  
  $('#msgbox2')
      .messager('')
      .done(function() { 
          $('#msgbox1')
              .messager(['End of demo.', 'Thank you.', 'Run again?'], 1000)
              .done(function() {
                  $('#msgbox1, #msgbox2').one('click', play); 
                  $('#msgbox2').messager('>');
              });
      });
}

play();
&#13;
html {
  background: rgba(0, 0, 0, 0.25);
}
#msgbox1, #msgbox2 {
  color: #FFF;
  padding: 0.3em 0.5em;
  font-size: 36pt;
  text-align: center;
  height: 1.8em;
  cursor: default;
}
#msgbox2 {
  color: yellow;
}
&#13;
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Queuing Messages with jQuery Deferred Object</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
  <div id="msgbox1"></div>
  <div id="msgbox2"></div>
</body>
</html>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

修改,更新

尝试

(function ($) {

    $.fn.messager = messager;

    function messager(message, speed, callback) {

        var that = $(this);
        if (that.data("queue") === undefined) {
            $.fx.interval = 0;
            that.data("queue", []);
            that.data("msg", []);
        };
        var q = that.data("queue"),
            msgs = that.data("msg");
        q.push([message, speed, callback]);
        msgs.push(message);

        var fn = function (m, s, cb) {

            return that.fadeOut(s, function () {
                that.text(m)
            })
                .delay(s)
                .fadeIn(s, cb)
                .promise("fx")
                .done(function (el) {
                console.log("callback", q.length);

                if (q.length > 1) {
                    q.splice(0, 1);
                    fn.apply(el, q[0])
                } else {
                    el.data("queue", []);
                    console.log("done", el.data("queue").length);
                    always(promise, ["complete", msgs])
                        .then(complete);
                }
                return el.promise("fx");
            })
        }

        , promise = $.when(!that.queue("fx").length 
                               ? fn.apply(that, q[0]) 
                               : that.promise("fx"))

        , always = function (elem, args) {
            if (elem.state() === "pending") {
                console.log(elem.state(), args)
            } else {
                if (elem.state() === "resolved") {
                    elem.done(function (elem) {
                        console.log(msgs.length + " messages complete");
                    })
                };
            };
            return elem.promise("fx")
        };

        always(promise, ["start", message, q.length]);

        return that
    };
}(jQuery));

请参阅.promise()

&#13;
&#13;
(function ($) {

    $.fn.messager = messager;

    function messager(message, speed, callback) {
        
        var that = $(this);
        if (that.data("queue") === undefined) {
            $.fx.interval = 0;
            that.data("queue", []);
            that.data("msg", []);
        };
        var q = that.data("queue"),
            msgs = that.data("msg");
        q.push([message, speed, callback]);
        msgs.push(message);

        var fn = function (m, s, cb) {

            return that.fadeOut(s, function () {
                that.text(m)
            })
                .delay(s)
                .fadeIn(s, cb)
                .promise("fx")
                .done(function (el) {
                console.log("callback", q.length);

                if (q.length > 1) {
                    q.splice(0, 1);
                    fn.apply(el, q[0])
                } else {
                    el.data("queue", []);
                    console.log("done", el.data("queue").length);
                    always(promise, ["complete", msgs])
                        .then(complete);
                }
                return el.promise("fx");
            })
        }
        
        , promise = $.when(!that.queue("fx").length 
                               ? fn.apply(that, q[0]) 
                               : that.promise("fx"))

        , always = function (elem, args) {
            if (elem.state() === "pending") {
                console.log(elem.state(), args)
            } else {
                if (elem.state() === "resolved") {
                    elem.done(function (elem) {
                        console.log(msgs.length + " messages complete");
                    })
                };
            };
            return elem.promise("fx")
        };

        always(promise, ["start", message, q.length]);

        return that
    };
}(jQuery));

            var complete = function() {
                if (!$("pre").is("*")) {
                    $("body").append("<pre>" + JSON.stringify($(this).data("msg"), null, 4))
                } else {
                    $("pre")
                    .text(JSON.stringify($(this).data("msg"), null, 4));  
                    $("label[for=messages]").text("messages updated")
                    .show(0).delay(350).hide(0)
                };
            };
    
    var fx = function() {
        $(this).css("color", "purple").animate({
            fontSize: "72"
        }, 100, function() {
            $(this).animate({
                fontSize: "36"
            }, 100, function() {
                $(this).css("color", "inherit")
            })
        })
    };
    
    var input = $("input");
    
    var $elem = $("#messages");
    $elem.messager("0", 1000)
    
    .messager("1", 100)
    .messager("2", 200)
    .messager("3", 300)
    .messager("4", 400)
    .messager("5", 500)
    .messager("6", 600)
    .messager("7", 700)
    .messager("8", 800)
    .messager("9", 900);
    
    $.each("abcdefghijklmnopqrstuvwxyz".split(""), function(key, val) {
        $elem.messager(val, 200, fx);
    });
    
    $("button").on("click", function() {
        $elem.messager(input.val().length > 0 ? input.val() : $.now(), 200);
        input.val("")
    });
&#13;
#messages {
    display:block;
    height:38px;
    font-size:36px;
    position : absolute;
}

label[for=messages] {
    color:blue;
}

pre {
  position:relative;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="button">add messages</label>&nbsp;<label for="messages"></label><br>
<input type="text" /><button>click</button>
<br />
<div id="messages">messages</div>
<br><br>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

<script>
(function ( $ ) {
$.fn.messager = function(message, effect, speed, gothru) {
    if (!$(this).data('message'))
    {
        $(this).data('message', Array());
    }
    $(this).data('message').push({messageContent: message, messageEffect: effect, messageSpeed: speed});
    if ($(this).data('message').length > 1 && gothru != true)
    {
        return;
    }
    $(this).fadeOut(speed).delay(speed).text(message).fadeIn(speed, function(){
        $(this).data("message").shift();
        if ($(this).data('message').length > 0)
        {
            var arrMessage = $(this).data('message');
            var messageContent = arrMessage[0].messageContent;
            var messageEffect= arrMessage[0].messageEffect;
            var messageSpeed= arrMessage[0].messageSpeed;
            $(this).data("message").shift();
            $(this).messager(messageContent , messageEffect, messageSpeed, true);
        }
    });
    return this;
};
}( jQuery ));
</script>

现在很好。

答案 3 :(得分:0)

以递归方式执行此操作的天真方式:

创建一个全局变量(布尔值),在本例中称为队列。如果queue为false,则将其设置为true并开始执行要运行的代码。该代码完成后,将队列设置为false。否则,如果queue为true,则只递归调用_this.messager(),直到将队列设置为false,这意味着代码已完成运行。

fadeIn()fadeOut()可以将回调作为最终参数,所以我在这里使用它。

HTML:

<div id="messageBox"></div>

JavaScript的:

(function ( $ ) {
  var queue = false;

  $.fn.messager = function(message, effect, speed) {
    var _this = $(this);

    if (!queue) {
        queue = true;
        _this.fadeOut(speed, function() {
          _this.text(message);
          _this.fadeIn(speed, function() {
            queue = false;
          });
        });
    } else {
        _this.messager(message, effect, speed);
    }
    return this;
  };
}( jQuery ));
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);

这通常会导致:

Uncaught RangeError: Maximum call stack size exceeded

更高级的例子:

这里我们创建一个名为counter的第二个变量来跟踪递归调用'messager'的次数,并且不超过选项中指定的限制。我将默认值设置为50,可以通过options参数覆盖。

此外,我们已经分离出您要运行的代码。这甚至可以是多个相互调用的函数,重要的是确保在代码完成运行时,将queue设置为false而不是返回false并将队列设置为函数的结果。将其设置为函数的结果只是使其未定义,直到函数完成返回。我们希望它在代码完成执行之前保持为真。

此示例还限制了递归调用,因此它每100毫秒只调用一次,尽管也可以通过options参数覆盖您喜欢的任何值(以毫秒为单位)。

HTML:

<div id="messageBox"></div>

JavaScript的:

(function( $ ) {
  var queue = false;
  var counter = 0;

  $.fn.messager = function(message, effect, speed, options) {
    var _S = $.extend({
        throttle: 100,
        counter: 50
    }, options);
    var _this = $(this);

    counter += 1;

    function codeToRun() {
      _this.fadeOut(speed, function() {
        _this.text(message);
        _this.fadeIn(speed, function() {
          queue = false;
        });
      });
    }

    if (!queue) {
      queue = true;
      codeToRun();
      counter = 0;
    } else {
      if (counter < _S.counter) {
        setTimeout(function() {
          _this.messager(message, effect, speed);
        }, _S.throttle);
      }
    }
    return this;
  };
})( jQuery );
$('#messageBox').messager('One', 300);
$('#messageBox').messager('Two', 300);
$('#messageBox').messager('Three', 300);

出于某种原因,直接在$(this)上调用方法会给我:

[Window, jquery: "1.11.0", constructor: function, selector: "", toArray: function, get: function…]

但是将$(this)存储在变量中并在该变量上调用方法会给我正确的元素:

[div#messageBox, selector: "#messageBox", context: document, jquery: "1.11.0", constructor: function, toArray: function…]