如何使用 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
在这个 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