我无法绕开承诺.我正在使用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会为最后一个地址执行动画)并且它们都会同时完成.如何在上一个完成之后将它们链接起来?
UPDATE
我用@ phtrivier的帮助来实现它:
$scope.addresses.reduce(function (curr,next) { return curr.then(function(){ return tourAddress(next.address) }); },Promise.resolve()).then(function(){ $log.log('all complete'); });
解决方法
你是对的,请求立即完成,因为调用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());
(这是使用下划线和蓝鸟的伪代码,但它应该是可适应的)