Chaining http calls in angular 2 in a for loop
14,750
Solution 1
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())
Solution 2
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:
- You are still using Observables and can chain whatever operators you want to.
- You are calling one http after another, which is your requirement.
- You don't need to worry about error handling, they are all chained to a single stream. Just call a
.catch
to your observables. - You can do anything to your recursion method (data manipulation,etc)
- You can set the condition when to terminate the recursion call.
- 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
Related videos on Youtube
Author by
AngularDebutant
Updated on June 04, 2022Comments
-
AngularDebutant almost 2 years
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
..-
DDelgro almost 7 yearsYou can send over all products as a list/array of product ids then do the actual adding on the backend code
-
AngularDebutant almost 7 yearsUnfortunately, I don't have access to backend..
-
Hadi Farhadi almost 7 years
productIds.forEach(productId => service.addProduct(productId)
not efficient way to do this. send all products to server in one request -
qqilihq almost 7 yearsHave a look at Angular's promises. This way you can chain your requests and run them sequentially. It's described here on SO in several questions, e.g. here: stackoverflow.com/questions/25704745/…
-
DDelgro almost 7 yearsIf you don't have access tot he backend then what @qqilihq suggested is spot on. You can do a loop once an asynchronous call/promise is fulfilled.
-
AngularDebutant almost 7 years@qqilihq I am using ngrx observable. I am trying not to use promises.
-
infamoustrey almost 7 yearsUse a callback function, keep track of the number of productIDs that have been processed with a simple counter and in the call back, decrement the counter.
-
Ismael Miguel almost 7 years@infamoustrey Or just shove them into an array, by
push()
ing into is andshift()
ing it. If the queue is clean, it stops.
-
-
CozyAzure almost 7 yearsforkJoin won't do it. He wants all the calls to be sequentials, not parallel.
-
AngularDebutant almost 7 yearsArent you removing the productId in that last line? so if productId initially is 1, then it will be loop([]) ..
-
Saravana almost 7 yearsI am passing the item removed from the array. There is an if check to stop the loop when all items have been processed. Like I said above you can implement that particular logic in many ways (using counters, etc). You can choose what you want.
-
AngularDebutant almost 7 yearsOh I see. Thank you sir.
-
AngularDebutant almost 7 yearsThank you boss. I'll have a look at it and report back to you.
-
AngularDebutant almost 7 yearsThis answer sounds great! I'll accept it if it works, because I like how it uses Observables operators.
-
AngularDebutant almost 7 yearsWhat's that
response
type you have there? -
Gili Yaniv almost 7 yearsResponse is the object type that http request returns. You can read about it here angular.io/api/http/Response
-
AngularDebutant almost 7 yearsOk! Then it's <Response> not <response> : )
-
CozyAzure almost 7 years@AngularDebutant glad that I helped
-
callback almost 7 yearsQuick question: Why didnt you use
take(productIds.length)
instead of recursion? -
CozyAzure almost 7 years@callback Yeah that would work too, but you still need a recursion.
.take()
is specifying the terminating condition of that recursion. -
callback almost 7 yearsYep, it will though save a couple of lines of code, and will make the code easy to understand IMO :)
-
CozyAzure almost 7 years@callback legit. Added the edit. I am just too used to ternary operator :)
-
Koushik Ravulapelli over 5 yearsThe above solution doesn't work for RxJS v5+ as the merge operator expects the comma separated values. So to make it work use
return Observable.merge(...productedsObservable)
.