【问题标题】:How to cancel a http.post() while it is waiting for a response?如何在等待响应时取消 http.post()?
【发布时间】:2018-10-30 02:16:50
【问题描述】:

这是来自 Angular 组件的一些代码。它是一个搜索组件,其中characterIndexes 是一个搜索结果数组。

通过在搜索框中输入搜索结果来检索搜索结果,该搜索框会根据其内容触发searchtriggersearchEmptyTrigger。在获得第一个结果后,我必须执行另一个 http.post() 来获取属于索引的名称,以便对它们进行排序(我从代码中省略了该部分)。然后将结果插入到characterIndex-array 中。

searchEmptyTrigger 被触发时会发生类似的事情。除了characterIndex-array 只是设置为空并且不需要 http-requests。

我遇到的问题是,在某些情况下,当searchEmptyTrigger 被触发时,searchtrigger 的代码仍在运行(由于 http-requests 的延迟)。

结果是characterIndexes 首先是空的。然后它会在收到searchtrigger中的http请求的结果后再次填满。

所以最大的问题是:'如何在等待回复时取消我正在运行的http.post()?'

  public characters: any[];
  public characterIndexes: number[];

  let searchBox = document.getElementById('search-box');
  let searchTrigger = fromEvent(searchBox, 'input')
  .pipe(
    map((event: any) => event.target.value ),
    filter( text => text.length > 2 ),
    debounceTime( 500 ),
    distinctUntilChanged(),
    switchMap( text =>  ajax(`https://esi.evetech.net/v2/search/?categories=character&datasource=tranquility&language=en-us&search=^${text}&strict=false`)
    )
  );

  let searchEmptyTrigger = fromEvent(searchBox, 'input')
  .pipe(
    map((event: any) => event.target.value ),
    filter( text => text.length <= 2 )
  );

  searchTrigger.subscribe( response => {
    if( response.response.character ){
      let characterIndexes = response.response.character;
        this.http.post('https://esi.evetech.net/latest/universe/names/?datasource=tranquility', characterIndexes)
        .subscribe( (charactersInfo: any[]) => {
           // do some stuff with this.characterIndexes and this.characters = [];
        });
      } else {
        this.characterIndexes = [];
        this.characters = [];
      }
    });

    searchEmptyTrigger.subscribe( () => {
      // reset values 
      this.characterIndexes = [];
      this.characters = [];
    });

PS:我也愿意接受与上述代码执行相同操作的替代方法,我可以在其中取消 http-request。

【问题讨论】:

    标签: angular rxjs angular6 rxjs5


    【解决方案1】:

    我认为你应该使用takeUntil(searchEmptyTrigger)

    this.http.post('https://esi.evetech.net/latest/universe/names/?datasource=tranquility', characterIndexes)
            .pipe(takeUntil(searchEmptyTrigger)) // make sure to cancel the post if `searchEmptyTrigger` emits
            .subscribe( (charactersInfo: any[]) => {
               // do some stuff with this.characterIndexes and this.characters = [];
            });
    

    PS,您可以将整个代码完全简化为更少的代码。

    避免将subscription 包含在另一个subscription 中,例如您的post 请求。

    您可以使用运算符合并它们。 我懒得写了,:d

    【讨论】:

    • 感谢您对嵌入式订阅的反馈。我很确定这是问题原因的一部分。我会阅读一些关于运营商的信息,并希望从那里找到更好的方法;)。
    【解决方案2】:

    试试这个方法……

    示例:概念是unsubscribe可观察对象...

    const request = this.searchService.search(this.searchField.value)
      .subscribe(
        result => { this.result = result.artists.items; },
        err => { this.errorMessage = err.message; },
        () => { console.log('Completed'); }
      );
    
      request.unsubscribe();  // <-- Hear you can cancel the API request.. 
      //Just set in when you need to cancel. It will works fine. 
      // E.x use with timeout or delay option of observable. 
    }
    

    【讨论】:

    • 我也有同样的直觉。但是对于 2 个连续请求,我无法确定第二个(请求后)是否正在运行。可能存在switchMap() 中的ajax( ... ) 仍在运行的情况。然后在 undefined 值上触发 .unsubscribe()。那么当第一个请求完成时,第二个请求仍然会发生。
    【解决方案3】:

    我想我明白了。我会先解释问题出在哪里,然后再讨论我的解决方案。

    问题

    在问题的代码中存在问题。在发出新值之前,我已经在 searchTrigger 中执行了 http 请求,但在 searchTriggerEmpty 中没有。因此,searchTrigger 可以在searchTriggerEmpty 甚至强硬的searchTrigger 先启动之后发出。

    解决方案

    解决方案是修复发射顺序。为此,必须在发出任何请求之前发出事件。当一个新事件被触发时,前一个事件应该被取消(switchMap-behavior)。

    代码解释

    • searchTrigger 在有人键入 searchBox
    • 添加了一些管道。 debounceTime( 500 )distinctUntilChanged() 防止事件过于频繁地触发。
    • map 管道返回搜索框中的值类型。
    • switchMap 内部有一个条件语句。这是选择进行的最佳方式的地方。
    • 如果字符串长于 2 个字符,它将返回一个可观察的字符索引数组(它通过执行一些 http 请求并处理它们来获取这些)。
    • 如果字符串少于 2 个字符,它将直接返回一个空数组。
    • 此处使用switchMap 的原因是,当发出新值时,当前挂起的进程将被取消。
    • 通过订阅searchTrigger,我们现在可以处理最新的搜索结果。
    • 我还提供了用于process_searchString() 的函数,以便您更好地了解它的作用/返回值。

    代码

    let searchBox = document.getElementById('search-box');
    let searchTrigger = fromEvent(searchBox, 'input')
    .pipe(
      debounceTime( 500 ),
      distinctUntilChanged(),
      map((event: any) => event.target.value ),
      switchMap( searchString => {
        if( searchString.length > 2 ){
          return this.process_searchString( searchString );
        } else if ( searchString.length <= 2 ) {
          return of( [] );
        }
      }),
    );
    
    searchTrigger.subscribe( ( characterIndexes: number[] ) => {
      this.characterIndexes = characterIndexes;
      this.characters = [];
      if( characterIndexes.length > 0 ){
        this.load_10characters();
      }
    });
    

    免责声明:在撰写本文时,我刚刚学会了使用 rxjs。因此,如果您看到任何需要改进的地方,请在评论中留言。

    PS:对于那些感兴趣的人来说,这个视频比我更好地解释了 switchMap(而且也很有趣):https://www.youtube.com/watch?v=rUZ9CjcaCEw

    额外process_searchString()

      private process_searchString( searchString: string ): Observable<number[]>{
        return new BehaviorSubject( searchString )
        .pipe(
          concatMap( ( text: string ) => this.request_characterSearch( text ) ),
          concatMap( ( response: any ) => {
            if( response.character ){
              return this.request_characterNames( response.character )
            } else {
              return of([]);
            }
          }),
          map( (charactersInfo: any[]) => this.sort_alphabetically( charactersInfo ) ),
          map( (charactersInfo: any[]) => charactersInfo.map( characterinfo => characterinfo.id ) ),
        );
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 2022-07-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-09
      • 2019-12-30
      相关资源
      最近更新 更多