如何制作一个延迟对象数组
How to make an array of Deferred objects
我是 Deferreds 和 Promises 的新手。
这是我的 [简化] 代码,它是在 JavaScript 对象中定义的:
myFunction: function(d, cb)
{
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
}).then(cb, cb);
},
flush: function(myArray)
{
return myFunction(myArray, myCallback);
}
以上工作正常。我可以调用 flush(someArray),一段时间后我得到 ajax 请求的结果。
问题:
我想修改 flush 函数,以便它首先将数组分成块(即更小的数组),然后在每个块上调用 myFunction。然后它必须return,一次性地,聚合数据(最好在一个数组中)来自每个ajax 拨打的电话。
我开始按照以下几行修改 flush(),但我知道这不太正确。请有人 complete/fill 帮我填补空白,或者建议一个行之有效的重组方案?
谢谢。
flush: function(myArray)
{
var chunk = 2;
var i, a;
var j = myArray.length;
var myArrayChunks = [];
for (i=0; i<j; i+=chunk)
{
a = myArray.slice(i, i+chunk);
myArrayChunks.push(a);
}
var myDeferreds = [];
for (i=0; i<myArrayChunks.length; i++)
{
// Here, I need to create a deferred object that will run: myFunction(myArrayChunks[i], myCallback)
// How do I do that?
var f = // The deferred object that will run: myFunction(myArrayChunks[i], myCallback)
myDeferreds.push(f);
}
return $.when.apply($, myDeferreds).then(function(){
// Here, I need to get the aggregated data that is returned by each of the deferreds. How do I do that?
console.log("FLUSH COMPLETE!");
});
}
我在下面粘贴的 async
库允许您 运行 一系列 async/deferred 请求,并将每个异步函数的结果传递给最终回调,这聚合结果集合。
特别是检查 parallel
方法,该方法将同时执行所有异步请求,但无法保证它们 运行 的顺序。如果您担心哪个为了执行您的异步请求,请查看 series
和 eachSeries
方法。
平行:
https://github.com/caolan/async#parallel
系列/每个系列:
https://github.com/caolan/async#seriestasks-callback
这两种方法都将您的结果聚合到一个最终结果对象中,该对象包含您进行的每个异步调用传递的所有结果。
注意,要使用 jQuery 的延迟功能,您需要在 async.parallel
的 "final" 回调中调用 .resolve()
或async.each
或 async.eachSeries
方法
下面是 parallel
方法的示例:
async.parallel([
function(callback){
// some request
$.ajax(/*details*/, function(data) {
callback(null, data);
});
},
function(callback){
// some request
$.ajax(/*details*/, function(data) {
callback(null, data);
});
}
],
// "final" callback, invoked after all above functions have
// called their respective callback() functions
function(err, results){
if(err) {
// handle error
} else {
// results contains aggregated results from all
// async calls (2nd parameter in callback(errorParam, resultsParam)
console.log('all async methods finished!', results);
}
});
这是一种传入数组并对每个数组元素创建异步方法的方法。 注意 async.each
方法中的每个异步调用都必须在异步请求得到解决时调用 callback()
,或者在你的异步错误方法中调用 callback(err)
如果有一个错误。如果将 N 个元素的数组传递给 async.each
方法,当所有 N async resolve callback()
方法已被调用。
async.each(array, function(element, callback) {
$.ajax(/* details */, {data: element}, function(data) {
// call `callback` when you're finished up
callback();
});
},
// "final" callback, invoked after each async call is resolved and
// invokes the callback() function
function(err){
if( err ) {
// handle errors
} else {
console.log('All async methods flushed!');
}
});
我喜欢这个图书馆,一旦你开始使用它,它就会改变你的生活:]。祝你好运!
由于您已经从 ajax 函数返回了一个承诺,我建议您使用承诺而不是简单的回调。这是一种方法:
myFunction: function(d) {
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
});
},
flush: function(myArray, chunkSize) {
chunkSize = chunkSize || 2;
var index = 0;
var results = [];
var self = this;
return jQuery.Deferred(function(def) {
function next() {
var start = index;
var arrayChunk, promises = [];
index += chunkSize;
if (index < myArray.length) {
arrayChunk = myArray.slice(start, chunkSize);
// create chunkSize array of promises
arrayChunk.forEach(function(item) {
promises.push(self.myFunction(item));
});
$.when.apply($, promises).then(function() {
// results are in arguments[0][0], arguments[1][0], etc...
for (var i = 0; i < arguments.length; i++) {
results.push(arguments[i][0]);
}
// next iteration
next();
}, def.reject)
} else {
def.resolve(results);
}
}
// start first iteration
next();
}).promise();
}
obj.flush(myArray).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
});
这是另一种方法,它是创建 $.ajax()
的一个版本,我称之为 $.ajaxChunk()
,它接受一个数据数组并为您进行分块。
// Send ajax calls in chunks from an array with no more than X in flight at the same time
// Pass in array of data where each item in dataArray is sent separately
// in an ajax call
// Pass settings.chunkSize to specify the chunk size, defaults to 2 if not present
// Returns a promise
// The resolved value of promise is an array of results
// The rejected value of the promise is whatever jQuery result failed first
$.ajaxChunk = function(dataArray, url, settings) {
settings = settings || {};
var chunkSize = settings.chunkSize || 2;
var index = 0;
var results = [];
return jQuery.Deferred(function(def) {
function next() {
var start = index;
var arrayChunk, promises = [];
index += chunkSize;
if (index < myArray.length) {
arrayChunk = myArray.slice(start, chunkSize);
// create chunkSize array of promises
arrayChunk.forEach(function(item) {
// make unique copy of settings object for each ajax call
var localSettings = $.extend({}, settings);
localSettings.data = item;
promises.push($.ajax(url, localSettings));
});
$.when.apply($, promises).then(function() {
// results are in arguments[0][0], arguments[1][0], etc...
for (var i = 0; i < arguments.length; i++) {
results.push(arguments[i][0]);
}
next();
}, def.reject)
} else {
def.resolve(results);
}
}
// start first iteration
next();
}).promise();
}
并且,示例用法:
$.ajaxChunk(arrayOfData, '/myURL', {
contentType: 'application/json',
dataType: 'json',
type: 'POST',
chunksize: 2
}).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
})
如果这里的真正要求是同时处理的调用不超过 X ajax 个,那么有一种更高效、更快速(端到端时间)的方法比分块做。相反,您可以随时跟踪 "in flight" 中有多少 ajax 调用,并且一旦一个完成,您就可以开始下一个。这比发送整个块然后等待整个块完成的分块更有效。我写了一个 jQuery helper 来实现这个:
$.ajaxAll = function(dataArray, url, settings, maxInFlight) {
maxInFlight = maxInFlight || 1;
var results = new Array(dataArray.length);
settings = settings || {};
var index = 0;
var inFlight = 0;
return jQuery.Deferred(function(def) {
function runMore() {
while (inFlight < maxInFlight && index < dataArray.length) {
(function(i) {
var localSettings = $.extend({}, settings);
localSettings.data = dataArray[index++];
++inFlight;
$.ajax(url, localSettings).then(function(data, textStatus, jqXHR) {
--inFlight;
results[i] = data;
runMore();
}, def.reject);
})(index);
}
// if we are all done here
if (inFlight === 0 && index >= dataArray.length) {
def.resolve(results);
}
}
// start first iteration
runMore();
}).promise();
}
注意:如果您为 maxInFlight
参数传递 1
,那么这将依次运行 ajax 调用。结果总是按顺序返回。
并且,示例用法:
$.ajaxAll(arrayOfData, '/myURL', {
contentType: 'application/json',
dataType: 'json',
type: 'POST'
}, 2).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
})
感谢大家的宝贵建议。
我在我的解决方案中结合了建议的技术。
关键是做出一个承诺数组,并将所需的调用(每个调用都有自己的数组块作为参数传递)推送给发出 ajax 请求的函数。我之前没有意识到的一件事是,这会在那一刻调用 ajaxCall() 函数,这没关系,因为它 returns 一个被推入数组的承诺。
在此之后,'when.apply' 行在等待所有 ajax promise 完成之前发挥作用。 'then' 函数的参数用于整理所需的所有结果(显然,确切的机制取决于返回参数的格式)。然后将结果发送到 theResultsHandler(),它取代了我在问题中首次发布的代码中的原始回调。
希望对其他无极新手有用!
ajax调用函数是:
ajaxCall: function(d) {
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
});
},
在 flush() 函数中...
var promises = [];
var i, j;
for (i=0; i<batchChunks.length; i++)
{
promises.push(self.ajaxCall(batchChunks[i]));
}
var results = [];
return $.when.apply($, promises).then(function(){
console.log("arguments = " + JSON.stringify(arguments));
for (i = 0; i < arguments.length; i++)
{
for (j = 0; j < arguments[i][0].length; j++)
{
results.push(arguments[i][0][j]);
}
}
return self.theResultsHandler(results);
});
我是 Deferreds 和 Promises 的新手。
这是我的 [简化] 代码,它是在 JavaScript 对象中定义的:
myFunction: function(d, cb)
{
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
}).then(cb, cb);
},
flush: function(myArray)
{
return myFunction(myArray, myCallback);
}
以上工作正常。我可以调用 flush(someArray),一段时间后我得到 ajax 请求的结果。
问题:
我想修改 flush 函数,以便它首先将数组分成块(即更小的数组),然后在每个块上调用 myFunction。然后它必须return,一次性地,聚合数据(最好在一个数组中)来自每个ajax 拨打的电话。
我开始按照以下几行修改 flush(),但我知道这不太正确。请有人 complete/fill 帮我填补空白,或者建议一个行之有效的重组方案?
谢谢。
flush: function(myArray)
{
var chunk = 2;
var i, a;
var j = myArray.length;
var myArrayChunks = [];
for (i=0; i<j; i+=chunk)
{
a = myArray.slice(i, i+chunk);
myArrayChunks.push(a);
}
var myDeferreds = [];
for (i=0; i<myArrayChunks.length; i++)
{
// Here, I need to create a deferred object that will run: myFunction(myArrayChunks[i], myCallback)
// How do I do that?
var f = // The deferred object that will run: myFunction(myArrayChunks[i], myCallback)
myDeferreds.push(f);
}
return $.when.apply($, myDeferreds).then(function(){
// Here, I need to get the aggregated data that is returned by each of the deferreds. How do I do that?
console.log("FLUSH COMPLETE!");
});
}
我在下面粘贴的 async
库允许您 运行 一系列 async/deferred 请求,并将每个异步函数的结果传递给最终回调,这聚合结果集合。
特别是检查 parallel
方法,该方法将同时执行所有异步请求,但无法保证它们 运行 的顺序。如果您担心哪个为了执行您的异步请求,请查看 series
和 eachSeries
方法。
平行:
https://github.com/caolan/async#parallel
系列/每个系列:
https://github.com/caolan/async#seriestasks-callback
这两种方法都将您的结果聚合到一个最终结果对象中,该对象包含您进行的每个异步调用传递的所有结果。
注意,要使用 jQuery 的延迟功能,您需要在 async.parallel
的 "final" 回调中调用 .resolve()
或async.each
或 async.eachSeries
方法
下面是 parallel
方法的示例:
async.parallel([
function(callback){
// some request
$.ajax(/*details*/, function(data) {
callback(null, data);
});
},
function(callback){
// some request
$.ajax(/*details*/, function(data) {
callback(null, data);
});
}
],
// "final" callback, invoked after all above functions have
// called their respective callback() functions
function(err, results){
if(err) {
// handle error
} else {
// results contains aggregated results from all
// async calls (2nd parameter in callback(errorParam, resultsParam)
console.log('all async methods finished!', results);
}
});
这是一种传入数组并对每个数组元素创建异步方法的方法。 注意 async.each
方法中的每个异步调用都必须在异步请求得到解决时调用 callback()
,或者在你的异步错误方法中调用 callback(err)
如果有一个错误。如果将 N 个元素的数组传递给 async.each
方法,当所有 N async resolve callback()
方法已被调用。
async.each(array, function(element, callback) {
$.ajax(/* details */, {data: element}, function(data) {
// call `callback` when you're finished up
callback();
});
},
// "final" callback, invoked after each async call is resolved and
// invokes the callback() function
function(err){
if( err ) {
// handle errors
} else {
console.log('All async methods flushed!');
}
});
我喜欢这个图书馆,一旦你开始使用它,它就会改变你的生活:]。祝你好运!
由于您已经从 ajax 函数返回了一个承诺,我建议您使用承诺而不是简单的回调。这是一种方法:
myFunction: function(d) {
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
});
},
flush: function(myArray, chunkSize) {
chunkSize = chunkSize || 2;
var index = 0;
var results = [];
var self = this;
return jQuery.Deferred(function(def) {
function next() {
var start = index;
var arrayChunk, promises = [];
index += chunkSize;
if (index < myArray.length) {
arrayChunk = myArray.slice(start, chunkSize);
// create chunkSize array of promises
arrayChunk.forEach(function(item) {
promises.push(self.myFunction(item));
});
$.when.apply($, promises).then(function() {
// results are in arguments[0][0], arguments[1][0], etc...
for (var i = 0; i < arguments.length; i++) {
results.push(arguments[i][0]);
}
// next iteration
next();
}, def.reject)
} else {
def.resolve(results);
}
}
// start first iteration
next();
}).promise();
}
obj.flush(myArray).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
});
这是另一种方法,它是创建 $.ajax()
的一个版本,我称之为 $.ajaxChunk()
,它接受一个数据数组并为您进行分块。
// Send ajax calls in chunks from an array with no more than X in flight at the same time
// Pass in array of data where each item in dataArray is sent separately
// in an ajax call
// Pass settings.chunkSize to specify the chunk size, defaults to 2 if not present
// Returns a promise
// The resolved value of promise is an array of results
// The rejected value of the promise is whatever jQuery result failed first
$.ajaxChunk = function(dataArray, url, settings) {
settings = settings || {};
var chunkSize = settings.chunkSize || 2;
var index = 0;
var results = [];
return jQuery.Deferred(function(def) {
function next() {
var start = index;
var arrayChunk, promises = [];
index += chunkSize;
if (index < myArray.length) {
arrayChunk = myArray.slice(start, chunkSize);
// create chunkSize array of promises
arrayChunk.forEach(function(item) {
// make unique copy of settings object for each ajax call
var localSettings = $.extend({}, settings);
localSettings.data = item;
promises.push($.ajax(url, localSettings));
});
$.when.apply($, promises).then(function() {
// results are in arguments[0][0], arguments[1][0], etc...
for (var i = 0; i < arguments.length; i++) {
results.push(arguments[i][0]);
}
next();
}, def.reject)
} else {
def.resolve(results);
}
}
// start first iteration
next();
}).promise();
}
并且,示例用法:
$.ajaxChunk(arrayOfData, '/myURL', {
contentType: 'application/json',
dataType: 'json',
type: 'POST',
chunksize: 2
}).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
})
如果这里的真正要求是同时处理的调用不超过 X ajax 个,那么有一种更高效、更快速(端到端时间)的方法比分块做。相反,您可以随时跟踪 "in flight" 中有多少 ajax 调用,并且一旦一个完成,您就可以开始下一个。这比发送整个块然后等待整个块完成的分块更有效。我写了一个 jQuery helper 来实现这个:
$.ajaxAll = function(dataArray, url, settings, maxInFlight) {
maxInFlight = maxInFlight || 1;
var results = new Array(dataArray.length);
settings = settings || {};
var index = 0;
var inFlight = 0;
return jQuery.Deferred(function(def) {
function runMore() {
while (inFlight < maxInFlight && index < dataArray.length) {
(function(i) {
var localSettings = $.extend({}, settings);
localSettings.data = dataArray[index++];
++inFlight;
$.ajax(url, localSettings).then(function(data, textStatus, jqXHR) {
--inFlight;
results[i] = data;
runMore();
}, def.reject);
})(index);
}
// if we are all done here
if (inFlight === 0 && index >= dataArray.length) {
def.resolve(results);
}
}
// start first iteration
runMore();
}).promise();
}
注意:如果您为 maxInFlight
参数传递 1
,那么这将依次运行 ajax 调用。结果总是按顺序返回。
并且,示例用法:
$.ajaxAll(arrayOfData, '/myURL', {
contentType: 'application/json',
dataType: 'json',
type: 'POST'
}, 2).then(function(results) {
// array of results here
}, function(jqXHR, textStatus, errorThrown) {
// error here
})
感谢大家的宝贵建议。
我在我的解决方案中结合了建议的技术。
关键是做出一个承诺数组,并将所需的调用(每个调用都有自己的数组块作为参数传递)推送给发出 ajax 请求的函数。我之前没有意识到的一件事是,这会在那一刻调用 ajaxCall() 函数,这没关系,因为它 returns 一个被推入数组的承诺。
在此之后,'when.apply' 行在等待所有 ajax promise 完成之前发挥作用。 'then' 函数的参数用于整理所需的所有结果(显然,确切的机制取决于返回参数的格式)。然后将结果发送到 theResultsHandler(),它取代了我在问题中首次发布的代码中的原始回调。
希望对其他无极新手有用!
ajax调用函数是:
ajaxCall: function(d) {
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
});
},
在 flush() 函数中...
var promises = [];
var i, j;
for (i=0; i<batchChunks.length; i++)
{
promises.push(self.ajaxCall(batchChunks[i]));
}
var results = [];
return $.when.apply($, promises).then(function(){
console.log("arguments = " + JSON.stringify(arguments));
for (i = 0; i < arguments.length; i++)
{
for (j = 0; j < arguments[i][0].length; j++)
{
results.push(arguments[i][0][j]);
}
}
return self.theResultsHandler(results);
});