简体   繁体   English

Angular HTTP 请求延迟拦截器未按预期工作

[英]Angular HTTP request delay interceptor not working as expected

I wanted to use an HTTP interceptor so that every HTTP request has a delay of 500ms between the next one.我想使用 HTTP 拦截器,以便每个 HTTP 请求在下一个请求之间有 500 毫秒的延迟。 I'm currently making these requests from an injectable service that is registered on the app.module and injected in my component.我目前正在从在 app.module 上注册并注入到我的组件中的可注入服务发出这些请求。 In the that same module I have registered my interceptor.在同一个模块中,我注册了我的拦截器。

// delay-interceptor.ts // 延迟拦截器.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 // app.module.ts

providers: [
    {
        provide: HTTP_INTERCEPTORS,
        useClass: DelayInterceptor,
        multi: true
    },
    ManageHousesService
]

// manage-houses.component.ts // 管理房屋.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 // 管理-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.因此,对于每个房子,我都会发出一个 POST 请求,并在订阅时使用新创建的房子的 ID 来创建房间。 For each room I make a POST request with the room information and the house ID.对于每个房间,我都会使用房间信息和房屋 ID 发出 POST 请求。 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.我想这可能与在每个 foreach 中调用相同的方法有关,这可能会重用相同的 observable 或类似的东西,因此不会触发 HTTP 拦截器,但我不确定。 On the interceptor I tried to use both the timer and the delay approach, but I got the same result with both approaches.在拦截器上,我尝试同时使用计时器和延迟方法,但两种方法都得到了相同的结果。

how do you suppose that each request will take maximum 500ms?您如何认为每个请求最多需要 500 毫秒? it may take longer than that可能需要更长的时间

did you try to use async/await ?您是否尝试使用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你可以使用await来处理这个异步代码,也最好避免在异步代码中使用forEach ,因为forEach不是promise-aware,这就是它的设计方式,所以最好使用普通for loop或 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我们还需要 git 像现在一样使用async/await摆脱订阅和取消订阅,我们需要处理promises而不是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为此, RxJS提供了toPromise()运算符,它将Observable转换为promise ,因此您可以使用promises而不是Observables来使用HttpClient方法

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). toPromise()返回一个Promise ,它使用该Observable发出的第一个值解析(它在内部为您调用subscribe并用Promise对象包装它)。

you can then update the createHouses function to be something like that然后,您可以将createHouses function 更新为类似

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希望它能按你的需要工作

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM