Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining http calls in angular 2 in a for loop

I have some code that looks like

//service.ts

addProduct(productId) {
   this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}

//component.ts

addAllproducts(productsIds) {
   productIds.forEach(productId => service.addProduct(productId);
}

What I want is to be able to wait for each call to finish before calling for the next productId, without using window.setTimeout ..

like image 729
AngularDebutant Avatar asked Jul 11 '17 12:07

AngularDebutant


Video Answer


2 Answers

How about some recursive calls using .expand()?

First, create a recursive function and map the data for recursive use:

const recursiveAddProduct = (currentProductId, index, arr)=>{
    return service.addProduct(currentProductId)
        .map((response)=>{
            return {
                data:response,
                index: index+1,
                arr:arr
            }
        })
};

Now, call it recursively in your component:

//productIds is an array of Ids    
//start of using the first index of item, where index = 0

let reduced = recursiveAddProduct(productIds[0],0,productIds)
    .expand((res)=>{
        return res.index>res.arr.length-1 ? Observable.empty(): recursiveAddProduct(productIds[res.index],res.index,productIds)
    });

reduced.subscribe(x=>console.log(x));

Here is a working JSBin

Benefit of using .expand operator:

  1. You are still using Observables and can chain whatever operators you want to.
  2. You are calling one http after another, which is your requirement.
  3. You don't need to worry about error handling, they are all chained to a single stream. Just call a .catch to your observables.
  4. You can do anything to your recursion method (data manipulation,etc)
  5. You can set the condition when to terminate the recursion call.
  6. One-liner (almost) code.

Edit

You can use .take() operator to terminate your recursion, if you don't like the inline ternary, like this:

let reduced = recursiveAddProduct(productIds[0],0,productIds)
    .expand(res=>recursiveAddProduct(productIds[res.index],res.index,productIds))
    .take(productIds.length)

Working JSBin

like image 182
CozyAzure Avatar answered Oct 11 '22 12:10

CozyAzure


First return the observable from your service method:

addProduct(productId) {
   return this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}

And use a recursive function and call it in the subscribe callback for each of the items in your array:

let loop = (id: number) => {
  service.addProduct(id)
    .subscribe((result) => {
      // This logic can be modified to any way you want if you don't want to mutate the `producIds` array
      if (productIds.length) {
        loop(productIds.shift())
      }
    })
}

loop(productIds.shift())
like image 33
Saravana Avatar answered Oct 11 '22 10:10

Saravana