我很难围绕jQuery承诺。我创建了以下片段来探索jQuery的承诺;通过此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>
我的理解是jQuery promises应该在执行b
时阻止执行c
和a
,并在c
执行时暂停执行b
我希望输出为a, b, c
,但我得到c, b, a
。请注意,我故意选择了这些延迟('a':5000,'b':2500,'c':0)来说明jQuery promises没有按计划阻止执行。
我错过了什么以及如何更改代码以获得预期的行为?
答案 0 :(得分:2)
警告:此答案仅针对I was expecting the output to be a, b, c but I'm getting c, b, a.
。它不能解决承诺问题。
在您的代码中,您希望的任何内容(如上文Michael Geary所述),&#39; a&#39; 5秒后输出,&#39; b&#39; 2.5秒后输出&#39; c&#39;立即输出。
如果你想要&#39; a&#39;在&#39; c&#39;之前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);
&#13;
.as-console-wrapper{max-height:100%!important;top:0;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
顺便说一句,更清晰的是你的代码,你可以更简单地调试它,理解它。寻找例子:
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);
&#13;
.as-console-wrapper{max-height:100%!important;top:0;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
答案 1 :(得分:1)
你出错的地方在于认为一个承诺会阻止执行。它没有。承诺只是编写回调函数的另一种方式;除了JavaScript本身提供的功能之外,它并没有增加任何魔力。 (我不是在这里处理async
/ await
,而是传统的&#34; JavaScript。)在调用任何回调之前,所有代码都会运行完毕。
你遇到麻烦的地方。在调用任何队列函数之前,while
循环运行完成:
while (queue.length > 0) {
d = d.then(queue.shift()); // you don't need the `.done`
}
如果你想让多个函数像你一样被执行超时调用,那么最好的方法就是不要像你现在的代码那样在一个gulp中激活所有setTimeout()
个调用。相反,让每个callback
函数启动下一个setTimeout()
。这样,您在任何时候只有一个超时挂起,当它触发时,您启动下一个超时。
我多年前写了一个jQuery插件,名为slowEach
。 (它并不真正依赖于jQuery,同样的技术也适用于非jQuery代码。)
代码没有使用promises - 它早于它们几年 - 但无论你使用promises还是传统的回调,原理都是一样的:从单个setTimeout()
开始,然后在它的回调函数时被叫,开始下一个setTimeout()
。这可以按照您希望简单while
循环的方式对超时进行排序。
原始slowEach()
代码可在this answer中找到。从那以后,有几个人做了improvements to the code。特别是here is a version添加onCompletion
回调,以便在处理整个数组时获得不同的回调。
此代码不使用promises,但更重要的是,可以使用。 :-)调整代码以使用promises是一个有趣的练习。
但是,不要再假设JavaScript会在执行下一行代码之前等待承诺完成。除非您正在使用async
/ await
,否则您正在运行的普通循环 - while
,for
,无论如何 - 将始终在<<}之前完成em>任何的回调 - 普通的回调或承诺 - 运行。
答案 2 :(得分:0)
我建议使用此jQuery扩展在延迟队列中进行顺序执行。
您可以使用超时函数列表作为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 : $(<iterable>).deferQueue(<function(element)>,<options>)
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>