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

在这个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]); } 

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

怎么能实现这一目标?

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

在下面的代码中, for语句已被删除,并且已经成为函数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(); }); 

看到这个说明在这个plunker 。

更新:

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

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

使用该更改,可以将promise添加到数组中。 然后使用spread运算符 (即... )将promise转换为$ .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);*/ }); }); 

嗯,实际上你在原帖中询问“ 如何实现这一点 ”但有人编辑了你的post… 1 https://api.jquery.com/jquery.post/#jqxhr-object

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

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