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

How to run the code sequentially using Deferred?

在这个 for 循环内部,我希望首先强制它到 运行 AJAX 代码块。结果(即 data.LocationId),我想将其保存在服务器上,然后 运行 使用 i[=26 的循环=] 减少。

如果你看到我的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]);
}

由于您的 ajax 调用似乎彼此独立(一个不依赖于另一个),您可以 运行 它们并行并使用承诺来保持result in order 这样您就可以按顺序处理结果。这通常是一个更快的端到端执行时间,但仍然可以让您按顺序处理结果。您可以使用 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
});

虽然我支持 jfriend00 关于 运行 并行处理请求的建议,但您特别要求:

How can this be achieved?

如果您真的想要它们 运行 serially/sequentially,一种技术是 运行 来自 success() 回调的下一次迭代.

在下面的代码中,for 语句已被删除,i 已成为函数 [=47] 的参数=]asyncProcess()。然后在成功回调中,如果i的值大于0,则在i减一后再次调用该函数(就像for 循环完成了)。

 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() function- passing an array of promises (e.g. Returned by $.ajax())。

看看this updated plunker. You will notice that the function asyncProcess has been updated to return the call to $.ajax(), which is a jqXHR object, which "implements the Promise interface"1.

使用该更改,可以将承诺添加到数组中。然后使用spread operator (i.e. ...) to pass the promises to $.when

$.when(...promises).done(function() { ... });

扩展运算符是在 ES-6 中添加的,因此 IE 等较旧的浏览器将不支持它。如果需要对此类浏览器的支持,apply can be used to call $.when 提供这些承诺。

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

.done() 调用 $.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);*/
    });
});

好吧,其实你问的是“如何做到这一点" 在 the original post 但有人编辑了你的 post... 1https://api.jquery.com/jquery.post/#jqxhr-object