如何制作一个延迟对象数组

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 方法,该方法将同时执行所有异步请求,但无法保证它们 运行 的顺序。如果您担心哪个为了执行您的异步请求,请查看 serieseachSeries 方法。

平行:

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

系列/每个系列:

https://github.com/caolan/async#seriestasks-callback

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

注意,要使用 jQuery 的延迟功能,您需要在 async.parallel 的 "final" 回调中调用 .resolve()async.eachasync.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);
    });