【问题标题】:How to run the code sequentially using Deferred?如何使用 Deferred 顺序运行代码?
【发布时间】:2017-03-23 20:52:04
【问题描述】:

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

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

异步进程

data.LocationId 7615

异步进程

data.LocationId 7614

异步进程

data.LocationId 7613

但实际上是:

异步进程

异步进程

异步进程

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 调用(每个调用似乎不依赖于前面的调用,所以看起来它们可以并行运行)?作为结果,你想从for 循环中得到什么?
  • 谢谢!只有两个目的:Ajax POST 将数据保存在数据库中,applyEdits 方法将几何图形保存在 ArcGIS Server 上。我终于用Sam Onela提供的方法完成了!
  • 呃,您不应该将选择的解决方案编辑到您的问题中。在堆栈溢出时,问题应该仍然是问题。答案就是答案。您通过用绿色复选标记标记“最佳答案”来表明您选择了哪个答案。这就是堆栈溢出的工作方式。
  • 哦!对不起!这是我第一次......
  • 但是有没有地方放最后的可行代码?

标签: javascript jquery ajax asynchronous dojo


【解决方案1】:

由于您的 ajax 调用看起来都是相互独立的(一个不依赖于另一个),您可以并行运行它们并使用 Promise 来保持结果的顺序,这样您就可以处理结果为了。这通常是更快的端到端执行时间,但仍然可以让您按顺序处理结果。你可以使用这样的 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
});

【讨论】:

  • 非常感谢!我应用了 Sam Onela 的更简单的方法,它奏效了,但我很感激你的回答,我会找时间测试和学习它。谢谢!
  • @Atlas - 当它们可以并行运行时,按顺序运行它们有点傻。它只是让事情需要更长的时间才能完成。
  • 但是快的过程需要慢过程的结果。那时它将没有任何价值。这就是为什么我必须一个接一个地运行它。
  • @Atlas - 我不知道“快速过程需要慢速过程的结果”是什么意思。我在你的问题中没有看到不同的过程。此代码收集所有结果,然后让您按顺序处理它们,然后在它们全部完成后执行您想做的任何其他事情。
  • 哦,我的意思是我以前的错误代码。循环比内部运行得快,所以没有结果。我没有时间测试你的,但我认为它比 Sam 的效果更好。如果我有时间我会稍后尝试。谢谢~
【解决方案2】:

虽然我支持 jfriend00 并行运行请求的建议,但您特别提出:

如何实现?

如果您真的希望它们串行/顺序运行,一种技术是从 success() 回调运行下一次迭代。

在下面的代码中,for 语句已被删除,i 已成为函数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() 函数 - 传递一组承诺(例如,由 $.ajax() 返回)。

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

使用该更改,可以将 Promise 添加到数组中。然后使用spread operator(即...)将承诺传递给$.when

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

扩展运算符是在 ES-6 中添加的,因此像 IE 这样的旧浏览器将不支持它。如果此类浏览器需要支持,apply 可用于调用带有这些承诺的$.when

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

在调用$.when().done() 回调中,每个参数都是一个数组,其中第一个元素是数据。综上所述,我们的代码如下:

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 中询问了“如何实现这一点”,但有人编辑了您的帖子。 ..1https://api.jquery.com/jquery.post/#jqxhr-object

【讨论】:

  • 这是我在 Stack Overflow 上的第一个问题。非常感谢!它终于起作用了,而且很容易理解。我可以将这个想法应用到我的其他类似任务中!
  • 因为我不是以英语为母语的人,也不善于清楚地提出我的问题。哈哈~
  • 为什么saveProject 变量在那里?它没有有用的价值,因为asyncProcess() 不返回任何东西。
  • @jfriend00 我保持原行不变,以防 OP 决定稍后使用该变量 - 如果这是 codereview.stackexchange.com,那么我会报告诸如清理变量之类的事情。
  • 它真的没有返回任何有用的东西。循环内的两个进程是我需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 1970-01-01
相关资源
最近更新 更多