Reputation: 1998
I wanted to use an HTTP interceptor so that every HTTP request has a delay of 500ms between the next one. I'm currently making these requests from an injectable service that is registered on the app.module and injected in my component. In the that same module I have registered my interceptor.
// delay-interceptor.ts
@Injectable()
export class DelayInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return timer(500).pipe(
delay(500),
switchMap(() => next.handle(request))
)
}
}
// app.module.ts
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: DelayInterceptor,
multi: true
},
ManageHousesService
]
// manage-houses.component.ts
createHouses() {
this.houses.foreach((house: House) => {
this.createHousesService.createHouse(house.name).subscribe(createdHouse => {
house.rooms.foreach((room: Room) => {
this.createHousesService.createRoom(house.id, room.name).subscribe();
});
});
});
}
// manage-houses.service.ts
createHouse(houseName: string): Observable<House> {
return this.httpClient.post(`${this.apiUrl}/houses`, { houseName: houseName });
}
createRoom(houseId: string, roomName: string): Observable<Room> {
return this.httpClient.post(`${this.apiUrl}/houses/${houseId}/rooms`, { roomName: roomName });
}
In my component I have to make requests in a nested way. I have a list of houses and for each house I want to create a list of rooms. So for each house I make a POST request and on the subscription I use the ID of the newly created house to create the rooms. For each room I make a POST request with the room information and the house ID. Now this is where the issue appears. Between each house request the delay is working, but between all the rooms of a house it is not, and I can't figure out why that's happening.
I suppose it might have something to do with calling the same method inside each foreach which will probably reuse the same observable or something similar, and thus not trigger the HTTP interceptor, but I'm not sure. On the interceptor I tried to use both the timer and the delay approach, but I got the same result with both approaches.
Upvotes: 1
Views: 2123
Reputation: 2427
It is not correctly to change the behavior of the interceptor, because it will affect all requests. You can do this directly from the component, or create a service for this prototype.
concat(
...this.houses.map((house: House) =>
this.createHousesService.createHouse(house.name).pipe(
delay(500),
concatMap((createdHouse) =>
concat(
...house.rooms.map((room: Room) =>
this.createHousesService
.createRoom(house.id, room.name)
.pipe(delay(500))
)
)
)
)
)
).subscribe();
Upvotes: 0
Reputation: 2184
how do you suppose that each request will take maximum 500ms? it may take longer than that
did you try to use async/await
?
you can use await
to handle this asynchronous code, also it's better to avoid using forEach
in asynchronous code, as forEach
is not a promise-aware, that's how it has been designed
so it's better to the use the normal for loop
or the ES6 for of loop
also we need to git rid of the subscribe and unsubscribe as now by using the async/await
, we need to deal with promises
instead of observables
for that, RxJS
provides the toPromise()
operator which converts an Observable
to a promise
so you can work with the HttpClient
methods using promises
instead of Observables
toPromise()
returns a Promise
that is resolved with the first value emitted by that Observable
(it internally calls subscribe
for you and wraps it with a Promise
object).
you can then update the createHouses
function to be something like that
async createHouses() {
for (const house of this.houses) {
// wait for the house to be added to db
await this.createHousesService.createHouse(house.name).toPromise();
// then loop over the rooms
for (const room of house.rooms) {
// wait for the room to be added to the db
await this.createHousesService.createRoom(house.id, room.name).toPromise()
}
}
}
hope it works as you needed
Upvotes: 1