0

Basically what I want is something like this:

this.accounts.forEach(account=> {
   this.myService.sendMessage("hello", account).subscribe(
   success => {
       if(success)
         updateProgress...   
   })
}

The problem with this code is that it executes everything asynchronously without waiting for the previous request. I was looking at solutions using merge and flatMap but I'm confused. I'd like to wait on the request completion before moving to next iteration. This causes a problem if I want to add a progress bar.

Solution:

Basing on the other answers this is what I've come up with:

let requestList = this.accounts.map((account, index) => {
    return this.myService.sendMessage("hello", account).map(success => {
            if (success)
            {
                // Update progress here
            }
            return success;
        });
    });

Observable.concat(...requestList).subscribe(null, null, () => {
    console.log("Complete");
});
4
  • It appears that you have it written above such that the updateProgress won't execute until the subscribe completes. So I'm not sure what you are asking? Commented Apr 28, 2017 at 0:44
  • @DeborahK it subscribes to all requests at once in a loop and it updates out of order. I'd like to wait for the previous request before sending other requests to make it behave more like synchronous. Commented Apr 28, 2017 at 0:45
  • I see. Thanks for clarifying. Could you create a plunker that illustrates the issue that we could try out? Commented Apr 28, 2017 at 0:48
  • @DeborahK it's late night here so I don't have much energy for plunker. Commented Apr 28, 2017 at 1:02

2 Answers 2

2

Concat operation will sequence your requests:

let accounts = ['1', '2', '3', '4' ];

var i = 8000;
let obsList$= accounts.map(x=> {
   i = i - 1000;
  return Rx.Observable.of(x).delay(i);
});

Rx.Observable.concat(...obsList$)
   .subscribe(x=>console.log(x))
  1. map accounts to create an array of Observables. Add a delay to each one to make sure that the first ones are the slowerst one (to make sure that sequence works.
  2. Concat operator accepts a sequence of observables. so use ... (spread operator) to convert array
  3. use concat operator to make sure that next observable emits only when the previous one is completed.
Sign up to request clarification or add additional context in comments.

Comments

1

Here is a sample technique which may work for you.

    // create an array of promise/resolves that will sequence the calls
    let resolveArray: Array<(value: boolean) => void> = new Array[this.accounts.length];

    this.accounts.forEach((account, index) => {
        new Promise<boolean>((resolve, reject) => {
            // add the promise to the array.
            resolveArray[index] = resolve;
        }).then(result => {
            this.myService.sendMessage("hello", account).subscribe(
            success => {
                if(success)
                    // updateProgress...   

                    // when finished, resolve the next promise in the array
                    if (index < this.resolveArray.length) {
                        this.resolveArray[index + 1](true);
                    }

            })
        });
    });

    // resolve the first promise to start the resolve sequence.
    resolveArray[0](true);

This works by creating an array of promises that then trigger your subscription as each one finished.

1 Comment

When I use this code - let resolveArray: Array<(value: boolean) => void> = new Array[this.accounts.length]; I get an error on Array is not a constructor TypeError: Array[this.users.length] is not a constructor

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.