简体   繁体   English

jQuery顺序调用多个setTimeout()

[英]jQuery calling multiple setTimeout() sequentially

I'm having a hard time wrapping my mind around jQuery promises. 我很难将我的想法包含在jQuery承诺中。 I've created the following snippet to explore jQuery promises; 我创建了以下片段来探索jQuery的承诺; informed by this StackOverflow entry. 通过此StackOverflow条目获知。

 let q = [ function () { setTimeout(function () { console.log("a - " + Date.now()); }, 5000); }, function () { setTimeout(function () { console.log("b - " + Date.now()); }, 2500); }, function () { setTimeout(function () { console.log("c - " + Date.now()); }, 0); } ]; function SerialCall(queue) { var d = $.Deferred().resolve(); while (queue.length > 0) { d = d.then(queue.shift()); // you don't need the `.done` } } SerialCall(q); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

My understanding is that jQuery promises should hold off executing b and c while a is executing and subsequently hold off executing c while b is executing. 我的理解是jQuery promise应该在a执行时阻止执行bc ,然后在b执行时阻止执行c

I was expecting the output to be a, b, c but I'm getting c, b, a . 我期待输出为a, b, c但我得到c, b, a Note that I deliberately picked these delays ('a': 5000, 'b': 2500, 'c':0) to illustrate the fact that jQuery promises aren't blocking execution as planned. 请注意,我故意选择了这些延迟('a':5000,'b':2500,'c':0)来说明jQuery promises没有按计划阻止执行。

What am I missing and how should I alter the code to get the expected behavior? 我错过了什么以及如何更改代码以获得预期的行为?

WARNING: this answer only addresses the I was expecting the output to be a, b, c but I'm getting c, b, a. 警告:这个答案只解决了I was expecting the output to be a, b, c but I'm getting c, b, a. . It not solves the promise issue. 它不能解决承诺问题。

In your code, whatever you hope for (as explained by Michael Geary above), 'a' outputs after 5 seconds, 'b' ouputs after 2.5 seconds and 'c' outputs immediatly. 在您的代码中, 无论您希望如何 (如上面的Michael Geary所解释),'a'在5秒后输出,'b'输出2.5秒后'c'立即输出。

If you want the 'a' to ouptut before the 'c', its waiting time (its timeout ) must be shorter. 如果你想要'a'在'c'之前ouptut,它的等待时间(它的超时 )必须更短。

 let queue = [ function () { let waitingTime = 0 ; setTimeout(function () { console.log("a - " + Date.now()); }, waitingTime); }, function () { let waitingTime = 2500 ; setTimeout(function () { console.log("b - " + Date.now()); }, waitingTime); }, function () { let waitingTime = 5000 ; setTimeout(function () { console.log("c - " + Date.now()); }, waitingTime); } ]; function SerialCall(queue) { var d = $.Deferred().resolve(); while (queue.length > 0) { d = d.then(queue.shift()); // you don't need the `.done` } } SerialCall(queue); 
 .as-console-wrapper{max-height:100%!important;top:0;} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

By the way, the clearer is your code, the simplier you can debug it, understand it. 顺便说一句,更清晰的是你的代码,你可以更简单地调试它,理解它。 Look for instance: 寻找例子:

 let queue = [ function () { waitForMe('a', 0) }, function () { waitForMe('b', 2500) }, function () { waitForMe('c', 5000) } ]; function SerialCall(queue) { var d = $.Deferred().resolve(); while (queue.length > 0) { d = d.then(queue.shift()); // you don't need the `.done` } } function waitForMe(letter, someTime) { return setTimeout(function () { console.log(letter +" - " + Date.now()); }, someTime) } SerialCall(queue); 
 .as-console-wrapper{max-height:100%!important;top:0;} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

Where you are going wrong is in thinking that a promise blocks execution. 你出错的地方在于认为一个承诺会阻止执行。 It doesn't. 它没有。 A promise is just another way to write a callback function; 承诺只是编写回调函数的另一种方式; it doesn't add any magic beyond what JavaScript natively offers. 除了JavaScript本身提供的功能之外,它不会增加任何魔力。 (I'm not addressing async / await here, just "traditional" JavaScript.) All of your code runs to completion before any callbacks get called. (我不是await这里解决async / await ,只是“传统的”JavaScript。)在调用任何回调之前,所有代码都会运行完成。

That's where you're running into trouble. 那就是你遇到麻烦的地方。 Your while loop runs to completion before any of your queue functions ever get called: while调用任何队列函数之前, while循环运行完成

while (queue.length > 0) {
    d = d.then(queue.shift()); // you don't need the `.done`
}

If you want to have multiple functions get called with timeouts like you're doing, the best way to do it is not to fire all the setTimeout() calls all in a single gulp as your code does now. 如果你想让多个函数像你正在做的那样超时调用,那么最好的方法就是不要像你现在的代码那样在一个gulp中激活所有的setTimeout()调用。 Instead, have each of the callback functions start the next setTimeout() . 相反,让每个callback函数启动下一个setTimeout() This way you only have a single timeout pending at any time, and when it fires you start the next one. 这样,您在任何时候只有一个超时挂起,当它触发时,您启动下一个超时。

I wrote a jQuery plugin many years ago for this called slowEach . 多年前我写了一个jQuery插件,名为slowEach (It doesn't really depend on jQuery and the same technique would work for non-jQuery code too.) (它实际上并不依赖于jQuery,同样的技术也适用于非jQuery代码。)

The code doesn't use promises - it predates them by several years - but the principle is the same whether you use promises or traditional callbacks: start with a single setTimeout() , and then when its callback function gets called, start the next setTimeout() . 代码不使用promises - 它早于它们几年 - 但无论你使用promises还是传统的回调,原理都是一样的:从一个setTimeout() ,然后当它的回调函数被调用时,启动下一个setTimeout() This sequences the timeouts in the manner you were probably hoping the simple while loop would do. 这可以按照您希望简单的while循环执行的方式对超时进行排序。

The original slowEach() code can be found in this answer . 原始的slowEach()代码可以在这个答案中找到。 Several people have made improvements to the code since then. 从那时起,有几个人对代码进行了改进 In particular, here is a version that adds an onCompletion callback so you get a different callback when the entire array has been processed. 特别是, 这是一个添加onCompletion回调的版本 ,因此在处理整个数组时会得到不同的回调。

This code doesn't use promises, but more importantly, it works . 此代码不使用promises,但更重要的是,它可以使用 :-) It would be an interesting exercise to adapt the code to use promises. :-)这将是一个有趣的练习,使代码适应使用promises。

But again, don't assume that JavaScript will wait for a promise to be done before it executes the next line of code. 但同样,不要认为JavaScript会在执行下一行代码之前等待承诺完成。 Unless you're using async / await , whatever ordinary loop you're running - while , for , whatever - will always run to completion before any of the callbacks - ordinary callbacks or promises - get run. 除非你使用async / await ,你正在运行的任何普通环路- whilefor ,不管是谁-普通回调或承诺- -获得运行将之前的任何回调的始终运行完成。

I suggest to use this jQuery extension to make sequential execution in a deferred queue. 我建议使用这个jQuery扩展在延迟队列中进行顺序执行。

You can use your list of timeout function as input of deferQueue, and execute them inside a main function (callable). 您可以使用超时函数列表作为deferQueue的输入,并在main函数(可调用)中执行它们。

 $.fn.deferQueue = function(callable, options){ options = Object(options); var it = (this.get() || [])[Symbol.iterator](); var stop = false, cond = null; var self = this; this.stop = function(){ stop=true; }; this.end = function(_cond){ cond = _cond }; var tid = 0; var iterate = function(){ if(tid) clearTimeout(tid); var o = it.next(); if(cond instanceof Function && cond(o.value)) return; if(o.done || stop) return; var d = callable.call(self, o.value); if(options.timeout) tid = setTimeout(function(){ d.reject('timeout'); }, options.timeout); if(options.success) d.done(options.success); if(options.fail) d.fail(options.fail); d[options.iterate || 'always'](iterate); } iterate(); return this; } function log(text){ console.log('Log: '+text); } function error(text){ console.log('Error: '+text); } let q = [ function (D) { setTimeout(function () { console.log("a - " + Date.now()); D.resolve('function 1'); }, 5000); }, function (D) { setTimeout(function () { console.log("b - " + Date.now()); D.resolve('function 2') }, 2500); }, function (D) { setTimeout(function () { console.log("c - " + Date.now()); D.resolve('function 3') }, 0); }, 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png', 'https://unreachabe_domain/image.png', 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png', null, 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png' ]; $(function(){ var display_timeout = parseInt($('#container').data('display-timeout')), loading_timeout = parseInt($('#container').data('loading-timeout')); $(q).deferQueue(function(e){ var D = $.Deferred(); if(typeof(e) == 'string') $('<img/>').attr('src',e).on('load',function(){ setTimeout(function(){ D.resolve(e); },display_timeout)}) .on('error', function(){ D.reject(e) }) .appendTo('#container'); else if(e instanceof Function) e(D); D.done(log).fail(error); return D; },{iterate:'always',timeout:loading_timeout}).end(function(e){ return e===null; }); }) 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <pre> usage : $(&lt;iterable&gt;).deferQueue(&lt;function(element)&gt;,&lt;options&gt;) options: timeout: int(seconds) iterate: string(always | done | fail) success: function fail: function </pre> <div id="container" data-display-timeout="1000" data-loading-timeout="3000"> </div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM