【问题标题】:Angular 4.3 Observables: how to retry with an interval and max retries?Angular 4.3 Observables:如何以间隔重试和最大重试次数?
【发布时间】:2017-10-15 11:30:48
【问题描述】:

我是 Angular 4.3 的新手。我有一个地图应用程序,它通过下载一系列小图像(“图块”)来呈现“地图层”。启动时,下载地图图层可能需要 0.1 - 5.0 秒。

当客户端收到两个或更多图块时,我需要触发一个事件。我原以为我可以通过订阅一个以 200 毫秒间隔重试多达 30 次的 Observable 来实现这一点。

到目前为止:

  public updateTileContainer() {
    this._getTileContainer().subscribe(container => {
      if (container) {
        console.log('* do stuff with container *');
      }
    });
  }

  private _getTileContainer(): Observable<any> {

    return Observable.interval(200)
      .mergeMap(function (n) {

        const tileImages = $('#mymap').find('img[src*=\'kml\']');

        console.log(`Retry ${n}: ${tileImages.length} tiles found`);

        if (!tileImages || tileImages.length < 2) {
          return Observable.of(null);
        }

        const tileContainer = getContainer(tileImages);

        return Observable.of(tileContainer);
      })
      .take(30);
  }

这行得通!有点。我得到这个输出:

Retry 0: 0 tiles found
Retry 1: 0 tiles found
Retry 2: 0 tiles found
Retry 3: 0 tiles found
Retry 4: 0 tiles found
Retry 5: 0 tiles found
Retry 6: 0 tiles found
Retry 7: 2 tiles found
* do stuff with container *
Retry 8: 3 tiles found
* do stuff with container *
Retry 9: 9 tiles found
* do stuff with container *
Retry 10: 12 tiles found
* do stuff with container *
etc.

我实际上想要做的是在“重试 7”处发出一个事件,然后停止发出。

如何在找到容器后立即将其更改为“完成” Observable?以及如何“跳过”重试 0 到 6 的发射事件,因为它们不是真正的事件?如果 Observable 重试了 30 次而没有找到容器,我可以从 Observable 中抛出错误吗?

【问题讨论】:

标签: angular observable


【解决方案1】:

使用.retry(count)

如果您的选择中少于 2 个图块,您可能会引发错误。然后retry() 30 次。

注意:在第 30 次重试后,实际上会发出错误,您需要处理它。但这可能是件好事(见下文)。

public updateTileContainer() {
  this._getTileContainer().subscribe(container => {
      console.log('* do stuff with container *');
  },error=>{
      console.log('no container found !');
  });
}

private _getTileContainer(): Observable <any> {
  return Observable.timer(200).map(()=>{
    const tileImages = $('#googlemap').find('img[src*=\'kml\']');
    if (tileImages.length <= 2)
      throw new Error("not enough tiles !");
    return getContainer(tileImages);
  })
  .retry(30);
}

您的解决方案和@JonStødle 确实有效。但请注意,如果您无法获得任何容器,您将不会收到通知。事实上,如果没有找到容器,Observable 就完成了,没有错误。

如果你想知道你找不到容器,处理错误可能是一件好事。

【讨论】:

  • 啊,所以retry(N) 会抑制这些错误,直到最后重试?而console.log('no container found !');只会被执行一次?
  • 没错,就是这样。
  • 我自己没有测试过,但它看起来很有用。无论如何,它让我更好地理解了 observables。
【解决方案2】:

与编程中的大多数事情一样,有多种方法可以实现相同的结果 - 这主要取决于偏好。

这是我的解决方案。我个人觉得它更具可读性:

private _getTileContainer(): Observable<any> {

return Observable.interval(200)
    .map(_ => $('#googlemap').find('img[src*=\'kml\']')) // 1
    .take(30) // 2
    .skipWhile(tiles = tiles.length < 2) // 3
    .map(tiles => getContainer(tiles)) // 4
    .take(1); // 5
}
  1. 获取图像块。
  2. 确保我们这样做的次数不超过 30 次。
  3. 跳过发出的信号,直到我们至少有 2 个图块。
  4. 获取容器。
  5. 只带 1 个容器。

【讨论】:

    【解决方案3】:

    这给了我我需要的东西,但我不确定是否有更好的方法

    public updateTileContainer() {
      this._getTileContainer().subscribe(container => {
    
          console.log('* do stuff with container *');
    
      });
    }
    
    
    private _getTileContainer(): Observable<any> {
    
      return Observable.interval(200)
        .concatMap(n => {
          let tileContainer: any = null;
    
          const tileImages = $('#googlemap').find('img[src*=\'kml\']');         
          if (tileImages.length >= 2) {
            tileContainer = getContainer(tileImages);
          }
    
          return Observable.of(tileContainer);
        })
        .take(30)
        .filter(res => res !== null)
        .take(1);
    }
    

    因此,从原始示例开始,主 Observable 在前 6 或 7 次重试时发出 nulls,但 filter() 阻止它们被传递给订阅者。 take(30) 在 30 次重试后停止主 Observable,而在 filter() 后的 take(1) 在第一次 非空 重试后停止 Observable。

    【讨论】:

      【解决方案4】:

      适用于 rxjs 版本 6.3.3

          import { map, catchError, retryWhen, take, delay, concat } from 'rxjs/operators';
          import { throwError } from 'rxjs';
      
      
      export class ApiEXT {
      
          static get apiURL(): string { return 'http://localhost:57886/api'; };
          static httpCLIENT: HttpClient;
      
       static POST(postOBJ: any, retryCOUNT: number = 0, retryITNERVAL: number = 1000) {
              return this.httpCLIENT
                  .post(this.apiURL, JSON.stringify(postOBJ))
                  .pipe(
                      map(this.handleSUCCESS),
                      retryWhen(errors => errors.pipe(delay(retryITNERVAL), take(retryCOUNT), concat(throwError("Giving up Retry.!")))),
                      catchError(this.handleERROR));
          }
      
      
        private static handleSUCCESS(json_response: string): any {
              //TODO: cast_and_return    
              return JSON.parse(json_response);
      
          }
      
       private static handleERROR(error: Response) {
              let errorMSG: string;
              switch (error.status) {
                  case -1: errorMSG = "(" + error.status + "/" + error.statusText + ")" + " Server Not Reachable.!"; break;
                  default: errorMSG = "(" + error.status + "/" + error.statusText + ")" + " Unknown Error while connecting with server.!"; break;
              }
              console.error(errorMSG);
              return throwError(errorMSG);
          }
      
      }
      

      StackBlitz Example

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-22
        • 1970-01-01
        • 1970-01-01
        • 2012-04-05
        相关资源
        最近更新 更多