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) {
//...
});
