如何使用自己的回调传递给延迟数组
How to pass to when array of deferred with their own callbacks
是否可以通过自己的回调传递给 $.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
});
成功和错误的回调应该在这些函数中定义,但只在所有调用的函数 return 结果之后执行。
可能吗?
您可以创建一个回调数组并在 $.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();
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
由于这不是内置于承诺中的任何类型的行为,因此您可以实现自己的行为。 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);
我使用 promises 的经验告诉我这是丑陋的代码,但我现在不确定如何让这种特定类型的解决方案工作得更干净。
就我个人而言,我想我只是让 SomeFunctionX return 既是一个承诺又是一个回调,这样回调就可以从我们真正知道事情已经完成的外部调用:
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]);
})
});
并且,您可以将它变成一个可重复使用的函数,您只需传递一组函数,这些函数return编辑了正确的数据结构(承诺和回调):
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。如果您正在使用已经创建和 return 诸如 $.get()
之类的承诺的异步操作,那么您不应该创建自己的延迟。那是一个promise anti-pattern。您应该 return 已经创建的承诺。
是否可以通过自己的回调传递给 $.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
});
成功和错误的回调应该在这些函数中定义,但只在所有调用的函数 return 结果之后执行。
可能吗?
您可以创建一个回调数组并在 $.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();
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
由于这不是内置于承诺中的任何类型的行为,因此您可以实现自己的行为。 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);
我使用 promises 的经验告诉我这是丑陋的代码,但我现在不确定如何让这种特定类型的解决方案工作得更干净。
就我个人而言,我想我只是让 SomeFunctionX return 既是一个承诺又是一个回调,这样回调就可以从我们真正知道事情已经完成的外部调用:
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]);
})
});
并且,您可以将它变成一个可重复使用的函数,您只需传递一组函数,这些函数return编辑了正确的数据结构(承诺和回调):
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。如果您正在使用已经创建和 return 诸如 $.get()
之类的承诺的异步操作,那么您不应该创建自己的延迟。那是一个promise anti-pattern。您应该 return 已经创建的承诺。