Promises 和 Observables 都为我们提供了抽象,帮助我们处理应用程序的异步性质。 by Günter 和 @Relu 明确指出了它们之间的区别。
由于一个代码 sn-p 值一千字,让我们通过下面的示例更容易理解它们。
感谢@Christoph Burgdorf the awesome article
Angular 使用 Rx.js Observables 而不是 Promise 来处理 HTTP。
假设您正在构建一个搜索功能,该功能应在您输入时立即显示结果。这听起来很熟悉,但这项任务会带来很多挑战。
- 我们不希望每次用户按键时都点击服务器端点。它应该用HTTP 请求的风暴淹没他们。基本上,我们只想在用户停止输入时点击它,而不是每次击键。
- 对于后续请求,请勿使用相同的查询参数点击搜索端点。
- 处理无序响应。当我们同时处理多个请求时,我们必须考虑它们以意外顺序返回的情况。想象一下,我们首先输入computer,停止,发出一个请求,我们输入car,停止,发出一个请求。现在我们有两个正在处理的请求。不幸的是,携带 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]);
}
}
我们正在注入Jsonp 服务,以针对具有给定搜索词的Wikipedia API 发出GET 请求。请注意,我们调用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上基于承诺的实现示例
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"/>
在我们的组件中,我们从@angular/form 创建一个FormControl 的实例,并将其公开为我们组件上名为 term 的字段。
在幕后,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) // 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));
}
}
针对我们的应用程序已经显示结果的搜索词发送另一个请求会浪费资源。为了达到预期的行为,我们所要做的就是在调用debounceTime(400) 之后立即调用distinctUntilChanged 运算符
查看Plunker上的Observable实现示例
关于处理乱序响应,请查看全文
http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
就我在 Angular 中使用 HTTP 而言,我同意在正常用例中使用 Observable 而不是 Promise 并没有太大区别。在实践中,这些优点都不是真正相关的。我希望将来能看到一些高级用例:)
了解更多