如何制作Deferred对象的数组

How to make an array of Deferred objects

本文关键字:数组 对象 Deferred 何制作      更新时间:2023-09-26

我是延期和承诺的新手。

这是我的[简化]代码,它在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。然后,它必须一次性返回每个ajax调用的聚合数据(最好是在数组中)。

我开始按照以下几行修改flush(),但我知道它不太正确。请有人帮我完成/填补空白,或者建议进行一次有效的重组?

谢谢。

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库允许您运行一系列异步/延迟请求,并将每个异步函数的结果传递给最终回调,后者聚合结果的集合。

特别是,检查一下parallel方法,它将同时执行所有异步请求,但不能保证它们将按哪个顺序运行。如果你关心执行异步请求的顺序,请检查serieseachSeries方法。

平行:

https://github.com/caolan/async#parallel

系列/每个系列:

https://github.com/caolan/async#seriestasks-回调

这两个方法都将结果聚合到一个最终结果对象中,该对象包含您进行的每个异步调用传递的所有结果。

注意,要使用jQuery的延迟功能,您需要在async.parallelasync.eachasync.eachSeries方法的"最终"回调中调用.resolve()

以下是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异步解析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函数返回了promise,因此我建议您使用promise而不是简单的回调。这里有一种方法:

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
})

如果这里真正的要求是同时处理的ajax调用不超过X个,那么有一种比分块更高效、更快(端到端时间)的方法。相反,您可以随时准确地跟踪"飞行中"有多少ajax调用,一旦一个调用完成,就开始下一个调用。这比分块更有效,在分块中,你发送整个块,然后等待整个块完成。我已经编写了一个jQuery助手来实现这一点:

$.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
})

感谢大家的宝贵建议。

我在解决方案中使用了建议的技术组合。

关键是制作一个promise数组,并将对发出ajax请求的函数所需的调用(每个调用都有自己的数组块作为参数传递)推送到它上面。我之前没有意识到的一件事是,这在那个时刻调用了ajaxCall()函数,这没关系,因为它返回了一个推送到数组上的promise。

在这之后,"when.apply"行完成了等待所有ajax承诺完成的任务。"then"函数的参数用于整理所需的所有结果(显然,其确切机制取决于返回参数的格式)。然后,结果被发送到ResultsHandler(),它取代了我在问题中第一次发布的代码中的原始回调。

希望这对其他Promise新手有用!

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);
    });