如何创建Deferred对象数组

时间:2016-05-06 17:28:05

标签: javascript arrays ajax jquery-deferred .when

Deferreds and 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。然后,它必须一次性返回每个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!");
    }); 
}

4 个答案:

答案 0 :(得分:0)

我下面粘贴的async库允许您运行一系列异步/延迟请求,并将每个异步函数的结果传递给最终回调,该回调汇总了一组结果

特别是,请查看parallel方法,该方法将同时执行所有异步请求,但无法保证它们将以何种顺序运行。如果您担心执行异步请求的顺序,查看serieseachSeries方法。

<强>平行

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

series / eachSeries:

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

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

注意,要使用jQuery的延迟功能,您需要在&#34; final&#34;中调用.resolve()。回调async.parallelasync.eachasync.eachSeries方法

以下是parallel方法的示例:

&#13;
&#13;
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);
    }
});
&#13;
&#13;
&#13;

这是一种传递数组并为每个数组元素生成异步方法的方法。 注意async.each方法中的每个异步调用必须在解析异步请求时调用callback(),或者如果出现错误则在异步错误方法中调用callback(err)。如果您将 N 元素数组传递给async.each方法,则当所有 N 异步解析callback()方法具有所有 N 时,将调用最终回调被援引。

&#13;
&#13;
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!');
    }
});
&#13;
&#13;
&#13;

我喜欢这个图书馆,一旦你开始使用它,它就会改变你的生活:]。祝你好运!

答案 1 :(得分:0)

由于你已经从ajax函数返回了一个promise,我建议你使用promises而不是普通的回调。这是一种方法:

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

答案 2 :(得分:0)

通过创建http://stevegaines.info/api/Exams?id=3&extra=0 http://stevegaines.info/api/Exams/4 http://stevegaines.info/api/values $http.get(url) .then(function (dataResponse) { $scope.Exams = dataResponse.data; }, function (error) { alert("error.statusText = " + error.statusText); });版本的$.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的呼叫,那么就会有更高效,更快速(端到端时间)的方式比分块。相反,你要跟踪&#34;在飞行中的确切数量ajax调用&#34;在任何时候,一旦完成,你就开始下一个。这比你发送整个块的块的效率要高一些,然后等待整个块完成。我编写了一个实现这个的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();
}

注意:如果为1参数传递maxInFlight,则会依次运行串联的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
})

答案 3 :(得分:0)

感谢所有人的好建议。

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

关键是要创建一个promises数组,然后将所需的调用(每个都有自己的数组块作为参数传递给它)发送到发出ajax请求的函数。我之前没有意识到的一件事就是在那个时刻调用了ajaxCall()函数,这很好,因为它返回了一个推送到数组上的promise。

在此之后,&#39; when.apply&#39; line在等待所有ajax承诺完成之前就可以了。然后&#39;然后&#39;函数用于整理所需的所有结果(显然,确切的机制取决于返回的参数的格式)。然后将结果发送到theResultsHandler(),它取代了我在问题中首次发布的代码中的原始回调。

希望这对其他Promise-novices有用!

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