和Promises
都Observables
为我们提供了抽象,帮助我们处理应用程序的异步特性。Günter和 @Relu清楚地指出了它们之间的区别。
由于代码片段相当于一千个单词,让我们通过下面的示例来更容易地理解它们。
感谢@Christoph Burgdorf 的精彩文章
Angular 使用 Rx.js Observables 而不是 Promise 来处理 HTTP。
假设您正在构建一个搜索功能,该功能应在您键入时立即显示结果。这听起来很熟悉,但这项任务会带来很多挑战。
- 我们不想每次用户按键时都访问服务器端点。它应该用大量的HTTP请求淹没它们。基本上,我们只想在用户停止打字时点击它,而不是每次击键时点击它。
- 不要为后续请求使用相同的查询参数来访问搜索端点。
- 处理无序响应。当我们同时处理多个请求时,我们必须考虑它们以意外顺序返回的情况。想象一下,我们首先输入Computer,停止,一个请求发出,我们输入car,停止,一个请求发出。现在我们有两个请求正在处理中。不幸的是,携带计算机结果的请求在携带汽车结果的请求之后返回。
该演示仅包含两个文件:app.ts
和wikipedia-service.ts
. 不过,在现实世界中,我们很可能会进一步拆分事物。
下面是一个基于 Promise 的实现,它不处理任何所描述的边缘情况。
wikipedia-service.ts
import { Injectable } from '@angular/core';
import { URLSearchParams, Jsonp } from '@angular/http';
@Injectable()
export class WikipediaService {
constructor(private jsonp: Jsonp) {}
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 })
.toPromise()
.then((response) => response.json()[1]);
}
}
我们正在注入服务,以使用给定的搜索词向维基百科 APIJsonp
发出GET请求。请注意,我们调用是为了从 an到 a 。最终以 a作为我们搜索方法的返回类型。toPromise``Observable<Response>``Promise<Response>``Promise<Array<string>>
app.ts
// check the plnkr for the full list of imports
import {...} from '...';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Wikipedia Search</h2>
<input #term type="text" (keyup)="search(term.value)">
<ul>
<li *ngFor="let item of items">{{item}}</li>
</ul>
</div>
`
})
export class AppComponent {
items: Array<string>;
constructor(private wikipediaService: WikipediaService) {}
search(term) {
this.wikipediaService.search(term)
.then(items => this.items = items);
}
}
这里也没有太多惊喜。我们WikipediaService
通过搜索方法向模板注入并公开其功能。该模板只是绑定到keyup并调用search(term.value)
.
我们解开 WikipediaService 的搜索方法返回的Promise的结果,并将其作为简单的字符串数组公开给模板,以便我们可以*ngFor
循环遍历它并为我们构建一个列表。
请参阅Plunker上基于 Promise的实现示例
Observables真正闪耀的地方
让我们更改代码,以便每次击键时不再敲击端点,而是仅在用户停止输入400 毫秒时发送请求
为了揭示这样的超能力,我们首先需要获取一个Observable<string>
带有用户输入的搜索词的 。我们可以利用 Angular 的formControl
指令,而不是手动绑定到 keyup 事件。要使用此指令,我们首先需要将其导入ReactiveFormsModule
到我们的应用程序模块中。
app.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [BrowserModule, JsonpModule, ReactiveFormsModule]
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
导入后,我们可以在模板中使用 formControl 并将其设置为名称“term”。
<input type="text" [formControl]="term"/>
FormControl
在我们的组件中,我们创建一个from实例@angular/form
,并将其公开为组件上名称 term 下的字段。
在幕后,term自动公开一个我们可以订阅的Observable<string>
as 属性。valueChanges
现在我们有了一个Observable<string>
,克服用户输入就像调用debounceTime(400)
我们的Observable
. 这将返回一个新值Observable<string>
,只有在 400 毫秒内没有出现新值时才会发出新值。
export class App {
items: Array<string>;
term = new FormControl();
constructor(private wikipediaService: WikipediaService) {
this.term.valueChanges
.debounceTime(400) // wait for 400 ms pause in events
.distinctUntilChanged() // ignore if next search term is same as previous
.subscribe(term => this.wikipediaService.search(term).then(items => this.items = items));
}
}
如果我们的应用程序已经显示了结果,那么再发送一个搜索词请求会浪费资源。要实现所需的行为,我们所要做的就是distinctUntilChanged
在调用后立即调用操作员debounceTime(400)
请参阅Plunker上Observable实现的示例
对于处理无序响应,请查看完整文章 http://blog.thoughtram.io/angular/2016/01/06/take-advantage-of-observables-in-angular2.html
就我在 Angular 中使用 HTTP 而言,我同意在正常用例中使用 Observable 与 Promise 没有太大区别。这些优点在实践中都没有真正相关。我希望将来能看到一些高级用例:)
了解更多