我无法绕开承诺。我正在使用Google Earth API对地址进行“浏览”。游览只是一个持续大约一分钟的动画,当一个完成时,下一个应该开始。
这是我的巡演功能:
var tourAddress = function (address) {
return tourService.getLatLong(address).then(function (coords) {
return tourService.getKmlForCoords(coords).then(function (kml) {
_ge.getTourPlayer().setTour(kml);
_ge.getTourPlayer().play();
var counter = 0;
var d = $q.defer();
var waitForTour = function () {
if (counter < _ge.getTourPlayer().getDuration()) {
++counter;
setTimeout(waitForTour, 1000);
} else {
d.resolve();
}
};
waitForTour();
return d.promise;
});
});
}
这看起来效果很好。它启动动画并返回一个在动画完成时解析的promise。现在我有了一系列地址,我想先做一些游览:
$scope.addresses.forEach(function (item) {
tourAddress(item.address).then(function(){
$log.log(item.address + " complete");
});
});
当我这样做时,它们都会同时执行(Google Earth会为最后一个地址执行动画)并且它们都会同时完成。如何在上一个完成之后将它们链接起来?
更新
我使用@ phtrivier的帮助来实现它:
$scope.addresses.reduce(function (curr,next) {
return curr.then(function(){
return tourAddress(next.address)
});
}, Promise.resolve()).then(function(){
$log.log('all complete');
});
答案 0 :(得分:6)
你是对的,请求立即完成,因为调用tourAddress(item.address)执行请求并且返回在请求完成时解析的Promise。
由于您在循环中调用tourAddress,因此会生成许多Promise,但它们并不相互依赖。
你想要的是调用tourAdress,获取返回的Promise,等待它被解析,然后再用另一个地址再调用tourAddress,等等。
手动,你必须写下这样的东西:
tourAddress(addresses[0])
.then(function () {
return tourAddress(addresses[1]);
})
.then(function () {
return tourAddress(addresses[2]);
})
... etc...
如果您想自动执行此操作(您不是第一个:How can I execute array of promises in sequential order?),您可以尝试将地址列表缩减为将执行整个链的单个Promise。
_.reduce(addresses, function (memo, address) {
return memo.then(function (address) {
// This returns a Promise
return tourAddress(address);
});
}, Promise.resolve());
(这是使用下划线和蓝鸟的伪代码,但它应该是可适应的)
答案 1 :(得分:0)
这是因为你是异步做事所以他们都会在同一时间运行。这个答案应该可以帮助你重写循环,这样每个地址都可以在下一个地址之后运行。