1

I'm running into an issue with some code for an Ionic 3 app.

Basically, I have a list of objects that all have a unique id. The unique id for each object must be sent through a GET request so that we can get the appropriate data back for each object from the server. This has to be done on a per object basis; I can't bundle them into one request because there is no API endpoint for that.

Therefore the objects are all stored in an array, so I've been trying to loop through the array and call the provider for each one. Note that the provider is returning an observable.

Since the provider is an asynchronous function the promise will resolve before the loop is finished, unless I time out the promise resolution. This defeats the whole point of the promise.

What is the correct way that I should go about doing this so that the looping provider calls are done before the promise resolves?

If I have an inner promise to resolve when the looping is done, won't it also resolve prematurely?

I also read that it is bad to have a bunch of observables open. Should I instead return each observable as a promise using toPromise()?

Here is the code to build the data:

asyncBuildData() {
    var promise = new Promise((resolve, reject) => {
        let completedRequests = 0;
        for (let i = 0; i < 10; i++) {
            this.provider.getStuffById(listOfStuff[i].id).subscribe(val => {
                list.push(val)
                completedRequests++;
            })
        }
        console.log('cp', completedRequests); // completedRequests = 0
        setTimeout(() => {
            console.log('cp', completedRequests); // completedRequests = 10
            let done = true;
            if (done) {
                resolve('Done');
            } else {
                reject('Not done');
            }
        }, 1500)
    })
    return promise;
}

Code from Provider:

getStuffById(stuffId) {
    let url = url + stuffId;
    return this.http.get(url)
        .map(res => res.json());
}
CozyAzure
  • 8,280
  • 7
  • 34
  • 52
thevengefulco
  • 309
  • 2
  • 13
  • You want to wait all observable complete? See https://stackoverflow.com/questions/41734921/rxjs-wait-for-all-observables-in-an-array-to-complete-or-error – Duannx Jan 29 '18 at 02:22

2 Answers2

1

Even though you can't bundle them into one request, you can still bundle them into one observable, of which those requests are fired in parallel, using .forkJoin():

buildData$() {
    let parallelRequests = listOfStuffs.map(stuff => this.provider.getStuffById(stuff.id));

    return Observable.forkJoin([...parallelRequests]);
}

and then in your component, you can just call:

buildData$.subscribe(val=>{
    //val is an array of all the results of this.provider.getStuffById()
    list =val;
})

Note that Obersvable.forkJoin() will for all requests to complete before emitting any values.

CozyAzure
  • 8,280
  • 7
  • 34
  • 52
0

If I understand correctly then the following code should get you on your way. This will execute a promise, one at a time, for each element in the array.

        var ids = [1,2,3,4,5];

        ids.reduce(function (promise, id) {
            return promise.then(function () {
                let url = url + id;
                return this.http.get(url)
                           .map(res => res.json());
            });
        }, Promise.resolve()).then(function(last) {
            // handle last result
        }, function(err) {
            // handle errors
        });

I tested this with a jQuery post and replaced it with yours from Ionic. If it fails then let me know.

mmamane
  • 353
  • 4
  • 11