关于rxjs:使用-RxJs-Observable-来避免-Angular-应用中的-Promise-使用

1次阅读

共计 4422 个字符,预计需要花费 12 分钟才能阅读完成。

咱们通过一个具体的例子来论述。

思考您正在构建一个搜寻输出掩码,该掩码应在您键入时立刻显示后果。

如果您已经构建过这样的货色,那么您可能会意识到该工作带来的挑战。

  1. 不要在每次击键时都点击搜寻端点

将搜寻端点视为您按申请付费。不论它是不是你本人的硬件。咱们不应该比须要的更频繁地敲击搜寻端点。基本上咱们只想在用户进行输出后点击它,而不是每次击键时点击它。

  1. 不要在后续申请中应用雷同的查问参数命中搜寻端点

假如您键入 foo,进行,键入另一个 o,而后立刻退格并返回到 foo。这应该只是一个带有 foo 一词的申请,而不是两个,即便咱们在搜寻框中有 foo 后从技术上讲进行了两次。

3. 解决乱序响应

当咱们同时有多个申请进行中时,咱们必须思考它们以意外程序返回的状况。思考咱们首先键入 computer,进行,申请收回,咱们键入 car,进行,申请收回。当初咱们有两个正在进行的申请。可怜的是,在为 car 携带后果的申请之后,为 computer 携带后果的申请又回来了。这可能是因为它们由不同的服务器提供服务。如果咱们不正确处理此类情况,咱们最终可能会显示 computer 的后果,而搜寻框会显示 car.

咱们将应用收费和凋谢的维基百科 API 来编写一个小演示。

为简略起见,咱们的演示将只蕴含两个文件:app.ts 和 wikipedia-service.ts。不过,在事实世界中,咱们很可能会将事件进一步拆分。

让咱们从一个基于 Promise 的实现开始,它不解决任何形容的边缘状况。

这就是咱们的 WikipediaService 的样子。

应用了 jsonp 这个 Angular HTTP 服务:

上图将来自 angular/http 库中的 jsonp 返回的对象,应用 toPromise 办法转换成了 promise.

简略地说,咱们正在注入 Jsonp 服务,以应用给定的搜索词针对维基百科 API 收回 GET 申请。请留神,咱们调用 toPromise 是为了从 Observable\<Response\> 到 Promise\<Response\>。通过 then-chaining 咱们最终失去一个 Promise\<Array\<string\>\> 作为咱们搜寻办法的返回类型。

到目前为止一切顺利,让咱们看看保留咱们的 App 组件的 app.ts 文件。

看一下 wiki service 如何被生产的:

这里也没什么惊喜。咱们注入咱们的 WikipediaService 并通过搜寻办法向模板公开它的性能。模板简略地绑定到 keyup 并调用 search(term.value) 利用 Angular 的很棒的模板援用性能。

咱们解开 WikipediaService 的搜寻办法返回的 Promise 的后果,并将其作为一个简略的字符串数组公开给模板,这样咱们就能够让 *ngFor 循环遍历它并为咱们构建一个列表。

可怜的是,这个实现没有解决咱们想要解决的任何所形容的边缘状况。让咱们重构咱们的代码,使其合乎预期的行为。

让咱们更改咱们的代码,不要在每次击键时敲击端点,而是仅在用户进行输出 400 毫秒时发送申请。这就是 Observables 真正闪耀的中央。Reactive Extensions (Rx) 提供了宽泛的运算符,让咱们能够扭转 Observables 的行为并创立具备所需语义的新 Observables。

为了揭示这样的超能力,咱们首先须要取得一个 Observable\<string\>,它携带用户输出的搜索词。咱们能够利用 Angular 的 formControl 指令,而不是手动绑定到 keyup 事件。要应用此指令,咱们首先须要将 ReactiveFormsModule 导入到咱们的利用程序模块中。

导入后,咱们能够在模板中应用 formControl 并将其设置为名称“term”。

<input type="text" [formControl]="term"/>

在咱们的组件中,咱们从 @angular/form 创立了一个 FormControl 的实例,并将其公开为组件上名称 term 下的一个字段。

在幕后,术语会主动公开一个 Observable\<string\> 作为咱们能够订阅的属性 valueChanges。当初咱们有了一个 Observable\<string\>,驯服用户输出就像在咱们的 Observable 上调用 debounceTime(400) 一样简略。这将返回一个新的 Observable\<string\>,它只会在 400 毫秒内没有新值呈现时才收回新值。

这个新的对象会在咱们冀望的工夫距离之后,才会产生新值。至于如何管制工夫距离,对前端开发人员来说是一个黑盒子。

export class App {
  items: Array<string>;
  term = new FormControl();
  constructor(private wikipediaService: WikipediaService) {
    this.term.valueChanges
             .debounceTime(400)
             .subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));
  }
}

正如咱们所说,对咱们的应用程序曾经显示后果的搜索词收回另一个申请将是一种资源节约。侥幸的是,Rx 简化了许多简直不须要提及的操作。为了实现所需的行为,咱们所要做的就是在咱们调用 debounceTime(400) 之后立刻调用 distinctUntilChanged 运算符。同样,咱们将返回一个 Observable\<string\>,但它疏忽了与前一个雷同的值。

解决无序响应

解决无序响应可能是一项辣手的工作。基本上,咱们须要一种办法来示意,一旦咱们收回新申请,咱们就不再对之前进行中的申请的后果感兴趣。换句话说:一旦咱们开始一个新的申请,就勾销所有先前的申请。正如我在结尾简要提到的,Observables 是一次性的,这意味着咱们能够勾销订阅它们。

这是咱们想要更改 WikipediaService 以返回 Observable\<Array\<string\>> 而不是 Promise\<Array\<string\>> 的中央。这就像删除 toPromise 并应用 map 而不是 then 一样简略。

search (term: string) {var search = new URLSearchParams()
  search.set('action', 'opensearch');
  search.set('search', term);
  search.set('format', 'json');
  return this.jsonp
              .get('http://en.wikipedia.org/w/api.php?callback=JSONP_CALLBACK', { search})
              .map((response) => response.json()[1]);
}

当初咱们的 WikipediaSerice 返回一个 Observable 而不是 Promise,咱们只须要在咱们的 App 组件中将 then 替换为 subscribe。

this.term.valueChanges
           .debounceTime(400)
           .distinctUntilChanged()
           .subscribe(term => this.wikipediaService.search(term).subscribe(items => this.items = items));

然而当初咱们有两个 subscribe 调用。这是不必要的简短,通常是须要代码重构的标记。好消息是,当初搜寻返回一个 Observable<Array<string>>,咱们能够简略地应用 flatMap 通过组合 Observables 来将 Observable<string> 投影到所需的 Observable<Array<string>> 中。

this.term.valueChanges
         .debounceTime(400)
         .distinctUntilChanged()
         .flatMap(term => this.wikipediaService.search(term))
         .subscribe(items => this.items = items);

你可能想晓得 flatMap 是做什么的,为什么咱们不能在这里应用 map。

答案很简略。map 操作符须要一个函数,它承受一个值 T 并返回一个值 U。例如一个承受一个字符串并返回一个数字的函数。因而,当您应用 map 时,您会从 Observable\<T\> 取得一个 Observable\<U\>。然而,咱们的搜寻办法自身会生成一个 Observable<Array>。因而,来自咱们在 distinctUntilChanged 之后的 Observable<string>,map 会将咱们带到 Observable<Observable<Array<string>>。这不是咱们想要的。

另一方面,flatMap 运算符须要一个函数,它承受一个 T 并返回一个 Observable\<U\> 并为咱们生成一个 Observable\<U\>。

留神:这并不完全正确,但它有助于简化。

这完全符合咱们的状况。咱们有一个 Observable\<string\>,而后应用一个函数调用 flatMap,该函数承受一个字符串并返回一个 Observable\<Array\<string\>>。

当初咱们曾经把握了语义,还有一个小技巧能够用来节俭一些打字的工夫。咱们能够让 Angular 间接在模板中为咱们解包,而不是手动订阅 Observable。咱们要做的就是在咱们的模板中应用 AsyncPipe 并公开 Observable\<Array\<string\>> 而不是 Array\<string\>。

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Wikipedia Search</h2>
      <input type="text" [formControl]="term"/>
      <ul>
        <li *ngFor="let item of items | async">{{item}}</li>
      </ul>
    </div>
  `
})
export class App {

  items: Observable<Array<string>>;
  term = new FormControl();

  constructor(private wikipediaService: WikipediaService) {
    this.items = this.term.valueChanges
                 .debounceTime(400)
                 .distinctUntilChanged()
                 .switchMap(term => this.wikipediaService.search(term));
  }
}

更多 Jerry 的原创文章,尽在:” 汪子熙 ”:

正文完
 0