最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS : chaining promises over forEach loop - Stack Overflow

programmeradmin0浏览0评论

I'm having trouble wrapping my head around promises. I'm using the Google Earth API to do a 'tour' of addresses. A tour is just an animation that lasts about a minute, and when one pletes, the next should start.

Here's my function that does a tour:

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

This seems to work pretty well. It starts the animation and returns a promise that resolves when the animation is plete. Now I have an array of addresses, and I want to do a tour foreach of them:

$scope.addresses.forEach(function (item) {
      tourAddress(item.address).then(function(){
          $log.log(item.address + " plete");
       });
 });

When i do this, they all execute at the same time (Google Earth does the animation for the last address) and they all plete at the same time. How do I chain these to fire after the previous one pletes?

UPDATE

I used @phtrivier's great help to achieve it:

 $scope.addresses.reduce(function (curr,next) {
      return curr.then(function(){
            return tourAddress(next.address)
      });
  }, Promise.resolve()).then(function(){
      $log.log('all plete');
  });

I'm having trouble wrapping my head around promises. I'm using the Google Earth API to do a 'tour' of addresses. A tour is just an animation that lasts about a minute, and when one pletes, the next should start.

Here's my function that does a tour:

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

This seems to work pretty well. It starts the animation and returns a promise that resolves when the animation is plete. Now I have an array of addresses, and I want to do a tour foreach of them:

$scope.addresses.forEach(function (item) {
      tourAddress(item.address).then(function(){
          $log.log(item.address + " plete");
       });
 });

When i do this, they all execute at the same time (Google Earth does the animation for the last address) and they all plete at the same time. How do I chain these to fire after the previous one pletes?

UPDATE

I used @phtrivier's great help to achieve it:

 $scope.addresses.reduce(function (curr,next) {
      return curr.then(function(){
            return tourAddress(next.address)
      });
  }, Promise.resolve()).then(function(){
      $log.log('all plete');
  });
Share Improve this question edited Sep 29, 2015 at 13:01 John Slegers 47.2k23 gold badges204 silver badges173 bronze badges asked Sep 30, 2014 at 15:46 JonesopolisJonesopolis 25.4k12 gold badges71 silver badges113 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

You're right, the requests are done immediately, because calling tourAddress(item.address) does a request and returns a Promise resolved when the request is done.

Since you're calling tourAddress in a loop, many Promises are generated, but they don't depend on each other.

What you want is to call tourAdress, take the returned Promise, wait for it to be resolved, and then call tourAddress again with another address, and so on.

Manually, you would have to write something like :

tourAddress(addresses[0])
  .then(function () {
     return tourAddress(addresses[1]);
  })
  .then(function () {
     return tourAddress(addresses[2]);
  })
  ... etc...

If you want to do that automatically (you're not the first one : How can I execute array of promises in sequential order?), you could try reducing the list of address to a single Promise that will do the whole chain.

_.reduce(addresses, function (memo, address) {

   return memo.then(function (address) {
         // This returns a Promise
         return tourAddress(address);   
   });

}, Promise.resolve());

(This is pseudo-code that uses underscore, and bluebird, but it should be adaptable)

It's because you're doing things asynchronously so they'll all run at the same time. This answer should help you rewrite your loop so each address is run after the next one.

Asynchronous Loop of jQuery Deferreds (promises)

发布评论

评论列表(0)

  1. 暂无评论