1

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.

4
  • 1
    This interceptor doesn't separate your requests by 500ms, instead it delay it in 500ms, it's different behaviors. Commented Apr 5, 2020 at 0:12
  • Ah, I see, I suppose that makes sense. Is it possible to separate requests by a given time amount with an interceptor? Commented Apr 5, 2020 at 0:16
  • Yes, it's possible, you need to use the interceptor to create a queue and fire one request of the queue every 500ms. I'm on mobile now, but as soon as possible I write an answer elaborating this idea. Commented Apr 5, 2020 at 2:31
  • I think you should try another solution for these nested way you made requests with try to use something like forkjoin or zip to accomplish the same result in a more reactive approach Commented Apr 5, 2020 at 3:49

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

Comments

0

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();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.