【问题标题】:Angular 10, rxjs - looping on subscription?Angular 10,rxjs - 订阅循环?
【发布时间】:2020-08-24 10:40:03
【问题描述】:

我正在向 API 发出多个并发 DELETE 请求,导致服务器端出现死锁。

如果我有固定数量的删除,我可以这样做:

myAPI.delete(id).subscribe(res => {
   myAPI.delete(id2).subscribe(res => {
      myAPI.delete(id3).subscribe(res => {
      })
   })
})

等待上一个删除成功完成,然后再创建一个新的(连续的)。

问题是我可以有从1N 的请求,所以我正在寻找类似于for 订阅指令的东西。

有什么优雅的方法可以解决这个问题吗?

【问题讨论】:

  • 你能试试递归函数吗?
  • 为什么要发送1 to n 请求,而不是将所有1...N 参数发送到server 并在那里处理
  • @BasheerKharoti 你是对的,但它是我无法控制的外部 API
  • 这是一个糟糕的 API 设计。你应该做这个服务器端。
  • @Iñigo 同意 Adrian Brand 的观点,您应该将所有 ID 传递给服务器,然后在那里执行所有删除操作,而不是从前端进行多次删除调用。

标签: angular rxjs


【解决方案1】:

如果您有需要删除的 id 数组,您可以将from 运算符与concatMaptoArray 结合使用。最后一个是在全部完成后才调用订阅监听器:

const ids = [ 1, 2, 3 ];

from(ids).pipe(
  concatMap((id) => deleteThis(id)),
  toArray()
).subscribe(() => {
   console.log('all deleted');
})

working stack 可观察到模拟请求

【讨论】:

    【解决方案2】:

    有多种方法。

    顺序请求

    您可以使用 RxJS range 运算符和映射运算符 concatMap 来顺序调用每个请求。

    import { range } from 'rxjs';
    import { concatMap } from 'rxjs';
    
    range(1, N).pipe(                      // <-- will emit 1-N numbers
      concatMap(id => myAPI.delete(id))    // <-- set the correct `id` for each request here
    ).subscribe(
      res => console.log(res),
      err => console.log(err),
    );
    

    由于我们使用concatMap 运算符,每个id 的请求将在前一个请求完成之前等待。

    您还可以针对不同的行为使用其他映射运算符,例如 switchMapflatMapexhaustMap。差异 b/n 他们here


    其他方法 - 并行请求

    完成并行请求

    您可以使用 RxJS 的 toArrayforkJoin() 运算符进行多个并发调用。它同时订阅多个可观察对象

    import { range, forkJoin } from 'rxjs';
    import { concatMap, toArray } from 'rxjs/operators';
    
    range(1, N).pipe(                      // <-- will emit 1-N numbers
      concatMap(id => myAPI.delete(id)),   // <-- set the correct `id` for each request here
      toArray(),                           // <-- buffer all notifications and emit once as an array
      concatMap(reqs => forkJoin(reqs))
    ).subscribe(
      res => console.log(res),
      err => console.log(err),
    );
    

    缓冲的并行请求

    大多数浏览器对单个域的最大并行请求数有硬性限制(例如 Chrome - 6)。如果你遇到这个问题,你可以使用 RxJS 的 bufferCount 操作符而不是 toArray 操作符来控制最大并行请求数。

    import { from, forkJoin } from 'rxjs';
    import { concatMap, bufferCount } from 'rxjs';
    
    range(1, N).pipe(                      // <-- will emit 1-N numbers
      concatMap(id => myAPI.delete(id)),   // <-- set the correct `id` for each request here
      bufferCount(6),                      // <-- adjust number of parallel requests here
      concatMap(reqs => forkJoin(reqs))
    ).subscribe(
      res => console.log(res),
      err => console.log(err),
    );
    

    【讨论】:

    • 但是如果它们是同时请求的,服务器无论如何都会超载,不能解决死锁问题,对吧?我试图使请求顺序
    • @Iñigo:我已经更新了答案以包含完整的顺序请求。
    • 我建议编辑此答案以仅包含 concatMap 解决方案,因为它是唯一提供所请求行为的解决方案,即顺序请求
    • @WillAlexander:好点子,但我没有直接删除其他答案,而是将顺序解决方案推到了顶部,将其他解决方案推到了底部。这样,人们也可以使用其他解决方案。
    【解决方案3】:

    另一种解决方案是使用concat 运算符:

    import { concat } from 'rxjs';
    import { toArray } from 'rxjs/operators';
    
    concat(
       ...[1, 2, 3].map(id => this.delete(id)),
    )
    .pipe(toArray())
    .subscribe(() => console.log('all deleted'))
    

    或不带toArray() 运算符:

    import { concat } from 'rxjs';
    
    concat(...[1, 2, 3].map(id => this.delete(id)))
      .subscribe(
        res => {
          console.log('one deleted')
        },
        err => {},
        () => {
          console.log('all deleted')
        })
    

    【讨论】:

    • 别忘了toArray() :)
    【解决方案4】:
    const N = 10;
    range(1, N)
      .pipe(concatMap(deleteById), ignoreElements())
      .subscribe({
          complete: () => console.log('Complete')
      });
    
    function deleteById(id: number): Observable<String> {
      return of(`deleted-${id}`).pipe(delay(1000));
    }
    

    如果你想同时发出多个请求:

    
    const N = 10;
    const CHUNKED = 3; // (3 requests same time) concat (3 requests same time) ...
    
    range(1, N)
      .pipe(
        bufferCount(CHUNKED),
        concatMap((ids) => forkJoin(ids.map(deleteById))),
        ignoreElements()
      )
      .subscribe({
        complete: () => console.log(`Complete`)
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-19
      • 2021-11-18
      • 1970-01-01
      • 2021-11-04
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      相关资源
      最近更新 更多