2014-09-30 25 views
7

Ho problemi a ricordare le promesse. Sto utilizzando l'API di Google Earth per fare un "tour" di indirizzi. Un tour è solo un'animazione che dura circa un minuto, e quando si completa, il prossimo dovrebbe iniziare.AngularJS: concatenamento di promesse per ogni ciclo

Ecco la mia funzione che fa un giro:

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

Questo sembra funzionare abbastanza bene. Avvia l'animazione e restituisce una promessa che si risolve al termine dell'animazione. Ora ho un array di indirizzi, e voglio fare un tour foreach di loro:

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

Quando faccio questo, tutti eseguono allo stesso tempo (Google Earth fa l'animazione per l'ultimo indirizzo) e completano tutti allo stesso tempo. Come faccio a incatenarli per farli sparire dopo aver completato quello precedente?

UPDATE

ho usato grande aiuto @ di phtrivier per realizzarla:

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

risposta

5

Hai ragione, le richieste sono fatte subito, perché chiamando tourAddress (item.address) fa una richiesta e restituisce una promessa risolto quando la richiesta è fatto.

Poiché si chiama tourIndirizzo in un ciclo, vengono generate molte promesse, ma esse non dipendono l'una dall'altra.

Quello che vuoi è chiamare tourAdress, prendere la promessa restituita, attendere che venga risolta, quindi chiamare nuovamente tourAddress con un altro indirizzo, e così via.

Manualmente, si dovrà scrivere qualcosa come:

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

Se si vuole fare che automaticamente (non sei la prima: How can I execute array of promises in sequential order?), si potrebbe provare a ridurre l'elenco di indirizzi per un single Promise che farà l'intera catena.

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

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

}, Promise.resolve()); 

(Si tratta di pseudo-codice che utilizza sottolineatura, e Bluebird, ma dovrebbe essere adattabile)

+1

ahh grazie, penso 'reduce' è quello che sto cercando. Vedrò se riesco a farlo funzionare – Jonesopolis

+1

capito, grazie ancora per la guida – Jonesopolis

+2

Perché il '_.reduce'? Si potrebbe tranquillamente fare 'address.reduce' –

0

È perché si sta facendo le cose in modo asincrono così faranno tutti corrono allo stesso tempo. Questa risposta dovrebbe aiutarti a riscrivere il tuo loop in modo che ogni indirizzo venga eseguito dopo il prossimo.

Asynchronous Loop of jQuery Deferreds (promises)

Problemi correlati