Home > Software engineering >  Multiple sequential api calls RxJs
Multiple sequential api calls RxJs

Time:01-21

I need to make sequential api calls using RxJs. The thing is that first Observable emits an array, for each item of this array I should set a custom url in order to make the next call. In the second call I should check some conditions and return Observable. My question is, how can I make a call for each element of array as it's url changes?

I tried something like this, but it only sets the url for the last item, not for each.

  private func(): Observable<boolean> {
    return this.service
      .getItems()
      .pipe(
        map((response) => response.items),
        mergeMap((result) => {
          result.map((order) =>
            this.orderService.setCustomUrl(order.number)
          );
          return this.orderService.getItems().pipe(
            mergeMap((response) => {
              if (response.items.some((item) => item.number === this.model.number)) {
                return of(true);
              } else {
                return of(false);
              }
            }),
          );
        })
      );
  }

CodePudding user response:

This is what is happening in your code:

        {
          // for each "result" array element, call "this.orderService.setCustomUrl(order.number)"
          result.map((order) =>
            this.orderService.setCustomUrl(order.number)
          );
          // assuming "setCustomUrl" is setting the one variable, this will cause only the last "order.number" to be set at this point here

          // then this last part executes once and returns the boolean only for the last order
          return this.orderService.getItems().pipe(
            mergeMap((response) => {
              if (response.items.some((item) => item.number === this.model.number)) {
                return of(true);
              } else {
                return of(false);
              }
            }),
          );
        }

I think the intention is to verify if any order returned by this.orderService.getItems() using order.number have the item number from this.model.number so you should instead do something like this:

first change your service call so it can use the order number like this this.orderService.getItems(order.number)

then the following: (I might have some syntax error here, but I think you will get the idea)

private func(): Observable<boolean> {
    return this.service
      .getItems()
      .pipe(
        swithchMap((response) => {
          response.items.map(order =>
// for each item on the initial response return an observable,
// mapping its result to a boolean as your initial function
            this.orderService.getItems(order.number).pipe(map(itemsResponse => 
              itemsResponse.items.some((item) => item.number === this.model.number))
            ))
          )
        }),
// this will get all the observable boolean values and only yield when all resolve
        concatAll(), 
// and this last step aggregates all boolean vals into one
        map(all => all.some(x => x)) 
        )

concatAll() in the docs

CodePudding user response:

The reason for this is that you invoke setCustomUrl() but you don't use .getItems() until after the map() has completed. So by the time you invoke .getItems() the service has received the last item from your .map().

You can use the following code to get the desired result.

function func(): Observable<boolean> {
  return this.service
    .getItems()
    .pipe(
      map((response) => response.items),
      switchMap(items => from(items).pipe(
        mergeMap(item=>{
          this.orderService.setCustomUrl(item.number);
          return this.orderService.getItems().pipe(
            map(response => response.items.some((item) => item.number === this.model.number))
          );
        }),
        toArray()
      )),
      map(items => items.some(item => item))
    );
}

The from() operator turns an array, and emits each value individually. This is where we pipe in and return the .some() boolean.

The toArray() takes all of those values and turns it back into an array.

Then we do one last map() to see if all items are true.

  •  Tags:  
  • Related