是否可以使用自己的回调传递给$.when
延迟数组?因此,我希望在解决所有延迟后调用每个回调,以便将它们传递给$.when
。
例如,在下面的示例中,在解决了适当的延迟后立即调用回调,并且在所有延迟完成后仅调用$.when
中的一般回调
var d1 = $.Deferred();
var d2 = $.Deferred();
d1.done(function(result){alert('d1 is done');});
d2.done(function(result){alert('d2 is done');});
$.when(d1, d2).done(function(result){
alert('here only general callback is called');
});
d1.resolve();
setTimeout(function(){d2.resolve();}, 3000);
修改 我需要在哪里使用它。假设我有几个功能:
function SomeFunction1(){
var d = $.Deferred();
//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});
//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someData1');
return d.promise();
}
function SomeFunction2(){
var d = $.Deferred();
//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});
//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someData2');
return d.promise();
}
...
function SomeFunctionN(){
var d = $.Deferred();
//if I define callback here it will call immediately after resolve
//d.done(function(result){
//// do something with result
//});
//$.ajax() or $.get() or $('#someID').load() or whatever else or just d.resolve
d.resolve('someDataN');
return d.promise();
}
然后在某些情况下,我只需要调用其中一个函数,在其他情况下只调用其中两个函数,在第三种情况下它们都是:
1: $.when(SomeFunction1()).done(function(result){
//callback defined inside SomeFunction1
});
2: $.when(SomeFunction1(), SomeFunction2()).done(function(result){
//callbacks defined inside SomeFunction1 and SomeFunction2
});
3: $.when(SomeFunction1(), SomeFunction2(), ... , SomeFunctionN()).done(function(result){
//callbacks defined inside SomeFunction1, ... , SomeFunctionN
});
成功和出错时的回调应该在这些函数中定义,但只有在所有被调用的函数返回结果后才执行。
有可能吗?
答案 0 :(得分:2)
由于这不是承诺中内置的任何行为,您可以实现自己的行为。当特定的承诺完成时,jQuery将调用.done()
(而不是在完成组承诺时),因此您必须以不同的方式指定回调。
一种方案是为每个承诺添加.cb
属性,当整个组完成后,将调用回调。然后,您可以创建$.when
的超集,该超集将查找该回调并在整个组完成时调用它:
$.whenAfter = function(promiseArray){
return $.when.apply($, promiseArray).then(function() {
var results = Array.prototype.slice.call(arguments);
promiseArray.forEach(function(p, index) {
if (p.cb) {
p.cb(results[index]);
}
});
return results;
});
}
或者,如果您不想将该属性添加到promise中,您可以传递一个与promiseArray对应的单独回调数组:
$.whenAfter = function(promiseArray, callbackArray){
return $.when.apply($, promiseArray).then(function() {
var results = Array.prototype.slice.call(arguments);
promiseArray.forEach(function(p, index) {
var cb = callbackArray[index];
if (cb) {
cb(results[index]);
}
});
return results;
});
}
如果您的回调必须保留在您的功能内部,并且在完成其他一系列操作之前您不想执行,那么我建议您传递承诺并执行这些回调当这个承诺得到解决时。
function SomeFunction1(p1){
var p2 = $.ajax(...);
// now wait for both our async operation and some other async operation
// to be done before carrying out the rest of our business
$.when(p1, p2).then(function(a1, a2) {
// now everything else is done too so we can carry out the rest of our business
});
// return p2 so other things can know when this ajax operation is done
return p2;
}
并且,你可以将这些中的多个结合起来:
var def = $.Deferred();
var p = def.promise();
$.when(SomeFunction1(p), SomeFunction2(p), SomeFunction3(p)).then(def.resolve, ref.reject);
我对承诺的体验告诉我这是一个丑陋的代码,但我目前还不确定如何使这种特殊类型的解决方案更清洁。
就个人而言,我认为我只是让SomeFunctionX同时返回一个promise和一个回调,因此可以从我们实际知道事情完成的外部调用回调:
function SomeFunction1(){
var p = $.ajax(...);
function callback() {
// do something here after we're done and others are done too
}
return {promise: p, callback: callback}
}
然后,在您要调用多个函数的位置,将它们放在一个数组中并迭代该数组,收集结果并在适当时调用回调:
var funcs = [SomeFunction1, SomeFunction2, SomeFunction3];
var callbacks = [];
var promises = funcs.map(function(fn) {
var retVal = fn();
callbacks.push(retVal.callback);
return retVal.promise;
});
$.when.apply($, promises).then(function() {
var args = Array.prototype.slice.call(arguments);
callbacks.forEach(function(cb, index) {
cb(args[index]);
})
});
并且,您可以将其转换为可重用的函数,您只需传递一组返回正确数据结构的函数(promise和callback):
function runAll(funcs) {
var callbacks = [];
var promises = funcs.map(function (fn) {
var retVal = fn();
// if it only returns only a thenable (not our data structure), then just return the promise
// this allows you to mix in functions that just return a promise
if (typeof retVal.then === "function") {
// assume no callback
callbacks.push(null);
return retVal;
}
callbacks.push(retVal.callback);
return retVal.promise;
});
return $.when.apply($, promises).done(function () {
try {
var args = Array.prototype.slice.call(arguments);
callbacks.forEach(function (cb, index) {
if (cb) {
cb(args[index]);
}
});
} catch(e) {
// if any callback throws an exception, then reject
return $.Deferred().reject(e);
}
});
});
var funcs = [SomeFunction1, SomeFunction2, SomeFunction3];
runAll(funcs).done(function(results) {
// all done here
}).fail(function(err) {
// some sort of error here
});
P.S。如果您正在使用已经创建并返回承诺(例如$.get()
)的异步操作,那么您不应该创建自己的延迟。那是promise anti-pattern。您应该只返回已经创建的承诺。
答案 1 :(得分:1)
您可以创建一个回调数组并循环遍历character
$.when()
var funcs = {},
promises = [],
callbacks = [];
// create 5 functions and promises
for (var i = 0; i < 5; i++) {
(function(i) {
funcs[i] = function() {
console.log('Data in func #' + (i + 1) + ':', this.data)
}
promises[i] = $.Deferred();
// push specific callback for each promise into array
promises[i].then(function(result) {
callbacks[i] = funcs[i].bind({
data: result
})
});
// random resolve times
setTimeout(function() {
promises[i].resolve('Promise #' + (i + 1))
}, Math.random() * 1500);
})(i)
}
$.when.apply(null, promises).done(function() {
console.log('start callbacks loop');
callbacks.forEach(function(fn) {
fn();
})
})