1
votes

I am attempting to make several HTTP requests in response to a click on an "Update All" button. My code looks like this:

let new_items:Observable<ApiResponse<ShoppingListItem>>[] = this.items
      .map(item => new ShoppingListItem(...))
      .map(item => this.shoppingListService.updateItem(item));


concat(new_items).subscribe(
  r => console.log(r);
);

this.shoppingListService.updateItem returns the Observable generated by calling httpClient.put.

When this code is run, I expect to see the value printed by console.log(r) to be of the type produced by the updateItem method. Instead, it's an Observable. My server logs indicate that no HTTP request was made. This seems to indicate that it wasn't subscribed to. My understanding of the concat docs is that each individual Observable passed to concat will be subscribed in sequence when the Observable before it completes.

If I replace concat with forkJoin I get the result I was expecting. I don't particularly care about order, so I am fine using forkJoin but I want to understand why using concat didn't work.

2
Interesting. It works as you expect if you spread the array concat(...new_items). It might be a bug with rxjs or a bug with their documentation :-). It sounds like it should allow an array as input, but I see what you've mentioned, that the subscription gets Observable not the emitted type. StackBlitz - BizzyBob
@BizzyBob That does seem to be the issue. Adding the spread operator changed the behavior to what I expected: concat(...new_items).subscribe() outputs the responses from the HTTP requests. Write this up as an answer and I'll accept it. - ryanmcfall

2 Answers

3
votes

What you're seeing is a little confusing but it's the expected behavior of concat() and forkJoin().

forkJoin() works with both array of Observables and expanded Observables as individual arguments:

forkJoin(obs1, obs2, obs3)
// or
forkJoin([obs1, obs2, obs3])

But with concat it's different. It only expects source Observables (or ObservableInput) as individual arguments like the following:

concat(obs1, obs2, obs3)

But if you pass an array like concat([obs1, obs2, obs3]) it takes the array as an ObservableInput and just iterates its items which is not what you want.

0
votes

Looking at RxJS's documentation we get the following description for the concat operator:

Creates an output Observable which sequentially emits all values from given Observable and then moves on to the next.

the forkJoin documentation expresses that it:

Accepts an Array of ObservableInput or a dictionary Object of ObservableInput and returns an Observable that emits either an array of values in the exact same order as the passed array, or a dictionary of values in the same shape as the passed dictionary.

Using forkJoin the resulting observable would emit once an array with all the resulting values of the updateItem response observables, whilst concat would emit once for each of the updateItem response observables.