如果在 1 秒内没有响应,如何添加到数组?

How to add to array if no response during 1 second?

我有一个监听器 requests/responses。

我曾尝试 运行 仅当请求超过 1 秒时才使用微调器:

 @Injectable()
export class LoadingInterceptor implements HttpInterceptor {
  private requests: HttpRequest<any>[] = [];

  constructor(private spinnerService: SpinnerService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    this.requests.push(req);
    this.spinnerService.isLoading.next(true);

    return new Observable((observer) => {
      next.handle(req).subscribe(
        (event) => {
          if (event instanceof HttpResponse) {
            this.removeRequest(req);
            observer.next(event);
          }
        },
        () => {
          this.removeRequest(req);
        },
        () => {
          this.removeRequest(req);
        }
      );
    });
  }

  private removeRequest(request: HttpRequest<any>) {
    const index = this.requests.indexOf(request);

    if (index >= 0) {
      this.requests.splice(index, 1);
    }

    this.spinnerService.loadingStop.next();
    this.spinnerService.loadingStop.complete();
    this.spinnerService.isLoading.next(this.requests.length > 0);
  }
}

旋转器服务是:

 constructor() {
    this.isLoading
      .pipe(debounceTime(100), delay(1000), takeUntil(this.loadingStop))
      .subscribe((status: boolean) => (this.loadingStatus = status));
  }

为此我添加了这个:

.pipe(debounceTime(100), delay(1000), takeUntil(this.loadingStop))

但这对我不起作用...如果响应超过 1 秒,如何显示微调器?

使用 iif 运算符立即停止加载。

拦截器应该是这样的:

constructor(private spinnerService: SpinnerService) { }

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  this.spinnerService.start(request.url);

  return next.handle(request).pipe(
    finalize(() => () => this.spinnerService.stop(request.url))
  );
}

这是加载服务:

@Injectable()
export class SpinnerService {
  private _loading: BehaviorSubject<boolean>;
  private _request: Set<string>;
  private _delayTime: number;

  constructor() {
    this._loading = new BehaviorSubject(false);
    this._request = new Set();
    this._delayTime = 1000;
  }

  isLoading(time?: number): Observable<boolean> {
    return this._loading.asObservable().pipe(
      // uses switchMap to cancel the previous event
      switchMap(isLoading =>
        // use iif to put delay only for true value
        iif(
          () => isLoading,
          of(isLoading).pipe(
            delay(time !== undefined ? time : this._delayTime),
          ),
          of(isLoading),
        ),
      ),
    );
  }

  start(request: string = 'default', delayTime?: number): void {
    if (delayTime !== undefined)
      this._delayTime = delayTime;

    this._request.add(request);
    this._loading.next(true);
  }

  stop(request: string = 'default'): void {
    this._request.delete(request);

    if (!this._request.size)
      this._loading.next(false);
  }
}

所以它应该在模板中查找

@Component({
  selector: 'my-app',
  template: `<div *ngIf="isLoading$ | async">loading...</div>`,
})
export class AppComponent  {
  isLoading$: Observable<boolean>;

  constructor(private spinnerService: SpinnerService) {
    this.isLoading$ = this.spinnerService.isLoading();
  }
}

为了防止加载指示灯闪烁(我省略了对多个请求的处理)。

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {

  constructor(private spinnerService: SpinnerService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> {
    this.spinnerService.start();
    return next.handle(req).pipe(finalize( () => this.spinnerService.stop()));
  }
}
旋转器服务中的

debounceTime(500) 可以解决问题:

export class SpinnerService {

  private readonly state = new BehaviorSubject<boolean>(true);
  readonly state$ = this.state.asObservable()
    .pipe(
       debounceTime(500), 
       distinctUntilChanged()
  );

  constructor() {}

  public start() {
    this.state.next(true);
  }

  public stop() {
    this.state.next(false);
  }
}

要查看此操作的组件:

export interface Post {
  id: string;
  title: string;
  body: string;
}

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css'],
})
export class PostsComponent implements OnInit {
  readonly posts$: Observable<Post[]> = this.httpClient
    .get<Post[]>('https://jsonplaceholder.typicode.com/posts')
    .pipe(shareReplay(1));

  readonly state$ = this.spinnerService.state$;

  constructor(
    private spinnerService: SpinnerService,
    private httpClient: HttpClient
  ) {}

  ngOnInit() {}
}

HTML:

<p>List of Posts</p>

<ng-container *ngIf="(state$ | async);  else printResult">
  <h1>Loading...</h1>
</ng-container>

<ng-template #printResult>
  <ng-container *ngIf="posts$ | async as posts">
    <p *ngFor="let post of posts">
      {{ post.title }}
    </p>
  </ng-container>
</ng-template>

通过拦截器的解决方案有些粗粒度。在某些时候,您可能需要更细粒度的解决方案。例如。显示多个并行 requests/components 的加载指示器。 Nil's blog post中给出了另一种解决方案。

您的问题有很多解决方案。希望对你有帮助。