Promises within a loop: TypeScript + Angular + IndexedDB

3.2k views Asked by At

I'm working on a data synchronization service that is pulling down data from a web service and then storing it in IndexedDB.

I'm doing something like this in my TypeScript Angular Service:

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

I am getting a post success in the console, along with each returned record key that I anticipate. The trouble is with the asynchronous calls to my DataService. The this.dataService.add function is what actually creates a transaction and adds records to IndexedDb.

The output in my console (and in the state of IndexedDB - has just one record with a key of 188) indicates that this code sample only calls the add function on my DataService for the last element in records.

Console output:

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  

How can I structure my code differently to make the for loop "wait" for each asynchronous call to be done?

1

There are 1 answers

12
rob On BEST ANSWER

Approach 1 (only works if you are targeting ES6)

if you are using TypeScript 1.5 you can just use let instead of var to declare your element and key variables:

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

The problem is that element and key are on the scope of the function not the for loop so every time the for loop iterates it is just replacing element and key variables instead of creating new variables. If you use let then the variables will be on the scope of the for loop.

See What's-new-in-TypeScript for more details.

Approach 2

Alternatively you could also use Array.forEach instead of a for loop as that would also fix the scoping problem:

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