Make a jquery function wait till it's previous call has been resolved

后端 未结 4 869
渐次进展
渐次进展 2020-12-21 22:31

So, I\'ve searched for this high and low and maybe I\'m just having trouble understanding jQuery\'s deferred function or I\'m completely on the wrong track. So any help woul

相关标签:
4条回答
  • 2020-12-21 23:03

    Edit, Updated

    Try

    (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));
    

    See .promise()

    (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("")
        });
    #messages {
        display:block;
        height:38px;
        font-size:36px;
        position : absolute;
    }
    
    label[for=messages] {
        color:blue;
    }
    
    pre {
      position:relative;
    }
    <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>

    0 讨论(0)
  • 2020-12-21 23:17

    jQuery Deferred Way

    jQuery Deferred object (roughly compromising CommonJS Promises API) can help us managing queued operations. Here is my implementation of queuing messages. You can pass through multiple messages as an array in one call, or synchronize different message boards easily because #messager() returns jQuery object itself but also wrapped as a promise object which will be resolved just when message(s) being displayed.

    (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();
    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;
    }
    <!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>

    0 讨论(0)
  • 2020-12-21 23:24
    <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>
    

    It's good now.

    0 讨论(0)
  • 2020-12-21 23:27

    The naive way of doing it recursively:

    Make a global variable(boolean), in this case called queue. If queue is false, set it to true and begin executing the code you want to run. When that code finishes, set queue back to false. Otherwise, if queue was true, just recursively call _this.messager() until queue is set back to false, which would mean that the code is finished running.

    fadeIn() and fadeOut() can take callbacks as the final argument, so I'm utilizing that here.

    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);
    

    This typically results in:

    Uncaught RangeError: Maximum call stack size exceeded
    

    A more advanced example:

    Here we create a second variable called counter to keep track of how many times 'messager' is called recursively and doesn't exceed the limit specified in the options. I set a default of 50, which can be overwritten by the options parameter.

    In addition, we've separated out the code that you want to run. This could even be multiple functions that call each other, the important bit is making sure that when your code is finished running, you set queue to false rather than returning false and setting queue to the result of the function. Setting it to the result of the function just makes it undefined until the function finishes returning. We want it to remain as true until the code is finished executing.

    This example also throttles the recursive calling so that it's only called once every 100 milliseconds, although that too can be overwritten with whatever value you like (in milliseconds) via the options parameter.

    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);
    

    For some reason, calling methods on $(this) directly gives me:

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

    But storing $(this) in a variable and calling methods on that variable gives me the correct element:

    [div#messageBox, selector: "#messageBox", context: document, jquery: "1.11.0", constructor: function, toArray: function…]
    
    0 讨论(0)
提交回复
热议问题