StackOverflow 上的讨论:What is the difference between Promises and Observables?

得赞最高的一个回答:1777 赞

当异步操作完成或失败时,Promise 会处理单个事件。

注意:有 Promise 库支持 cancellation 操作,但 ES6 Promise 到目前为止还不支持。

Observable

一个 Observable 就像一个 Stream(在许多语言中),允许传递零个或多个事件,其中为每个事件调用回调。

通常 Observable 比 Promise 更受欢迎,因为它提供了 Promise 的特性等等。使用 Observable,您是否要处理 0、1 或多个事件并不重要。您可以在每种情况下使用相同的 API。

Observable 还比 Promise 具有可取消的优势。如果不再需要对服务器的 HTTP 请求或其他一些昂贵的异步操作的结果,Observable 的订阅允许取消订阅,而 Promise 最终会调用成功或失败的回调,即使你不这样做不再需要通知或它提供的结果。

虽然 Promise 会立即启动,但 Observable 只有在您订阅它时才会启动。这就是为什么 Observable 被称为懒惰的原因。

Observable 提供了 map、forEach、reduce 等运算符,用法类似于数组。

还有一些强大的操作符,如 retry() 或 replay() 等,它们通常非常方便。

延迟执行允许在通过订阅执行 observable 之前建立一系列操作符,以进行更具声明性的编程。

排名第二的回答:374 赞

举例说明。

Angular 使用 Rx.js Observables 而不是 promises 来处理 HTTP。

假设您正在构建一个搜索功能,该功能应在您键入时立即显示结果。 听起来很熟悉,但这项任务会带来很多挑战。

我们不想在用户每次按下一个键时都访问服务器端点,如果这样做的话,服务器会被大量的 HTTP 请求淹没。 基本上,我们只想在用户停止输入后触发 HTTP 请求,而不是每次击键时触发。

对于后续请求,不要使用相同的查询参数访问搜索端点。

处理无序响应。 当我们同时有多个请求进行中时,我们必须考虑它们以意外顺序返回的情况。 想象一下,我们首先键入 computer,停止,发出请求,然后键入 car,停止,发出请求。 现在我们有两个正在进行的请求。 不幸的是,携带结果给computer 的请求在携带结果给 car 的请求之后返回。

首先看如何用 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 循环遍历它并为我们构建一个列表。

Where Observables really shine

让我们更改我们的代码,不要在每次击键时敲击端点,而是仅在用户停止输入 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 400ms 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 运算符。

Observable 和 promise 的比较:

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

03-05 23:34