如何使用Deferred顺序运行代码?

时间:2017-03-23 20:52:04

标签: javascript jquery ajax asynchronous dojo

在这个for循环中,我希望强制它首先运行AJAX代码块。结果(即 data.LocationId ),我想将其保存在服务器上,然后在 i 减少的情况下运行循环。

如果你看到我的console.log,我希望它可能是:

  

asyncProcess

     

data.LocationId 7615

     

asyncProcess

     

data.LocationId 7614

     

asyncProcess

     

data.LocationId 7613

但实际上是:

  

asyncProcess

     

asyncProcess

     

asyncProcess

     

data.LocationId 7615

     

data.LocationId 7614

     

data.LocationId 7613

如何实现这一目标?

这是我的代码:

for (i = projectAreaSet.length-1; i >= 0; i--)
{
    function asyncProcess(geometry)
    {
        console.log("asyncProcess");
        var deferred = new Deferred(); //Dojo, not jQuery

        var locationProfile = { ProjectId: projectId }
        $.ajax({
            type: "POST",
            data: JSON.stringify(locationProfile),
            url: "api/LocationProfile/Create",
            contentType: "application/json",
        })
        .success(function (data)
        {
            LocationIdSet.push(data.LocationId);
            console.log("data.LocationId ", data.LocationId);
            var currentProjectGraphic = new esri.Graphic(geometry, newSymbol, attributes =
                { "ID": data.LocationId, "Type": 1}, null);
            var currentLayer = that.map.getLayer("Project");
            currentLayer.applyEdits([currentProjectGraphic], null, null);
            deferred.resolve();
        });
        return deferred.promise;
    }
    var saveProject = asyncProcess(projectAreaSet[i]);
}

2 个答案:

答案 0 :(得分:1)

由于你的ajax调用似乎彼此独立(一个不依赖于另一个),你可以并行运行它们并使用promises来保持结果的顺序,这样你就可以处理结果有序。这通常是一个更快的端到端执行时间,但仍然可以让您按顺序处理结果。你可以使用像这样的jQuery承诺来做到这一点:

var promises = [];
for (var i = projectAreaSet.length - 1; i >= 0; i--) {
    (function(geometry) {
        promises.push($.ajax({
            type: "POST",
            data: JSON.stringify({ProjectId: projectId}),
            url: "api/LocationProfile/Create",
            contentType: "application/json"
        }).then(function(data) {
            // make resolved value be data and geometry together so we can
            // process them in order together later
            return {geometry: geometry, data: data};
        }));
    })(projectAreaSet[i]);
}
$.when.apply($, promises).then(function() {
    var results = Array.prototype.slice.call(arguments);
    // process results array here in the order they were requested
    results.forEach(function(obj) {
        var data = obj.data;
        var geometry = obj.geometry;
        LocationIdSet.push(data.LocationId);
        console.log("data.LocationId ", data.LocationId);
        var currentProjectGraphic = new esri.Graphic(geometry, newSymbol, attributes = {
            "ID": data.LocationId,
            "Type": 1
        }, null);
        var currentLayer = that.map.getLayer("Project");
        currentLayer.applyEdits([currentProjectGraphic], null, null);
    });
    // all results processing done here - can run any final code here
});

答案 1 :(得分:0)

虽然我支持jfriend00关于并行运行请求的建议,但你特别问:

  

如何实现这一目标?

如果你真的希望它们按顺序/顺序运行,那么一种技术就是从 success()回调中运行下一次迭代。

在下面的代码中, for 语句已被删除, i 已成为函数 asyncProcess()的参数。然后在成功回调中,如果 i 的值大于0,则在从 i 中减去一个值后再次调用该函数(就像 i 一样/ em>循环确实)。

 var i = projectAreaSet.length - 1;
 asyncProcess(projectAreaSet[i], i);
 function asyncProcess(geometry, i) {
      $.ajax({
          type: "POST",
          //...other options
       })
      .success(function(data) {
           LocationIdSet.push(data.LocationId);
           //instantiate new esri.Graphic, call currentLayer.applyEdits()
           //then run the next iteration, if appropriate
           if (i > 0) {
               asyncProcess(projectAreaSet[i-1], i-1);
           }
           deferred.resolve();
       });

请参阅this plunker中演示的内容。

更新

在阅读了答案的讨论之后,看起来你的目标是采用平行的方法。为此,由于正在使用jQuery,请查看使用.when()函数 - 传递一组promise(例如,$.ajax()返回)。

看看this updated plunker。您会注意到函数 asyncProcess 已更新,以返回对$.ajax()的调用,...是一个jqXHR对象,“实现了Promise接口 1

使用该更改,可以将promise添加到数组中。然后使用spread operator(即$.when(...promises).done(function() { ... }); )将承诺传递给$.when

$.when.apply(null, promises).done(function() { ... });

在ES-6中添加了传播运算符,因此IE之类的旧版浏览器不支持它。如果此类浏览器需要支持,apply可用于使用这些承诺调用$.when

$.when()

在对var promises = []; for (i = projectAreaSet.length - 1; i >= 0; i--) { promises.push(asyncProcess(projectAreaSet[i], i)); } $.when(...promises).done(function() { Array.prototype.forEach.call(arguments, function(response) { var data = response[0]; console.log("data.LocationId ", data.LocationId); LocationIdSet.push(data.LocationId); geometry = projectAreaSet[data.i]; /* continue with normal code: var currentProjectGraphic = new esri.Graphic(geometry, newSymbol, attributes = { "ID": data.LocationId, "Type": 1}, null); var currentLayer = that.map.getLayer("Project"); currentLayer.applyEdits([currentProjectGraphic], null, null);*/ }); }); 的调用的 .done()回调中,每个参数都是一个数组,其中第一个元素是数据。总而言之,我们有如下代码:

GetSystemMenu

嗯,实际上你在the original post中问了“ 如何实现这个目标 ”但有人编辑了你的交... 1 <子> https://api.jquery.com/jquery.post/#jqxhr-object