javascript - Promises i en loop: TypeScript + Angular + IndexedDB


Jag arbetar på en datasynkroniseringstjänst som hämtar data från en webbtjänst och sedan lagrar det i IndexedDB.

Jag gör något liknande i min TypeScript Angular-tjänst:

this.http
    .post(postUrl, postData)
    .success((data: any, status, headers, config) => {

         console.log("post success");
         console.log("results:");
         console.log(data);

         var records = data.Records;
         var promiseStack = [];

         for (var i = 0; i < records.length; i++) {

             var element = records[i];
             var key = this.dataService.getKeyFromElement(objectStoreConfig, element);

             console.log("key: " + key);

             this.dataService.keyExists(objectStoreConfig, key).then((update) => {

                 if(update){
                     // TODO: put
                     promiseStack.push(true);
                 } else {
                     console.log("using dataService to add to objectStore...");
                     console.log("adding: " + key);
                     this.dataService.add(element, objectStoreConfig).then((result) => {
                         promiseStack.push(result);
                     });
                 }
             });

         }

         this.q.all(promiseStack).then((result) => {
             console.log("done adding/updating all.");
             deferred.resolve(result);
         });
    })
    .error((data, status, headers, config) => {
    });

Jag får en post success i konsolen, tillsammans med varje returnerat record key som jag förväntar mig. Problemet ligger i de asynkrona anropen till min DataService. Funktionen this.dataService.add är det som faktiskt skapar en transaktion och lägger till poster i IndexedDb.

Utdata i min konsol (och i tillståndet för IndexedDB - har bara ett enda register med nyckeln 188) indikerar att detta kodexempel endast anropar funktionen add på min DataService för det sista elementet i records.

Konsolutdata:

post success
results:
Object
key: 78 
key: 194
key: 188
done adding/updating all. 
using dataService to add to objectStore...
adding: 188
using dataService to add to objectStore...
adding: 188
using dataService to add to objectStore...
adding: 188  

Hur kan jag strukturera min kod annorlunda för att få for-loopen att "vänta" på att varje asynkront anrop ska bli klart?

Svar #1

Metod 1 (fungerar endast om du riktar dig mot ES6)

om du använder TypeScript 1.5 kan du bara använda let istället för var för att deklarera dina element- och nyckelvariabler:

let element = records[i];
let key = this.dataService.getKeyFromElement(objectStoreConfig, element);

Problemet är att element och key finns i funktionens scope och inte i for-loopens, så varje gång for-loopen itererar ersätter den bara variablerna element och key istället för att skapa nya variabler. Om du använder let kommer variablerna att finnas i for-loopens scope.

Se Vad's nytt i TypeScript för mer information.

Metod 2

Alternativt kan du också använda Array.forEach istället för en for-loop eftersom det också skulle lösa scopingproblemet:

records.forEach(function(element) {
    //...
});

Relaterade artiklar