协慌网

登录 贡献 社区

Promises 和 Observables 有什么区别?

有人可以解释 Angular 中PromiseObservable之间的区别吗?

每个例子都有助于理解这两种情况。在什么情况下我们可以使用每个案例?

答案

诺言

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

注意:有Promise库支持取消,但 ES6 Promise到目前为止还没有。

可观察

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

通常ObservablePromisePromise因为它提供了Promise等功能。使用Observable ,如果要处理 0,1 或多个事件并不重要。您可以在每种情况下使用相同的 API。

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

Observable 提供了mapforEachreduce操作符 ,类似于数组

还有强大的运算符,如retry()replay() ,... 通常非常方便。

PromisesObservables为我们提供了抽象,帮助我们处理应用程序的异步性质。 @Günter 和 @Relu 清楚地指出了它们之间的区别。

由于代码片段胜过千言万语,让我们通过下面的示例更容易理解它们。

感谢 @Christoph Burgdorf 的精彩文章


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

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

  • 我们不希望每次用户按下某个键时都会点击服务器端点,它应该充斥着一连串的HTTP请求。基本上,我们只想在用户停止键入而不是每次按键时都点击它。
  • 对于后续请求,请勿使用相同的查询参数命中搜索端点。
  • 处理无序响应。当我们同时在飞行中有多个请求时,我们必须考虑他们以意想不到的顺序返回的情况。想象一下,我们先输入电脑 ,停止,请求消失,我们打 ,停止,请求消失。现在我们有 2 个飞行请求。不幸的是,带有计算机结果的请求在带有汽车结果的请求之后返回。

该演示将只包含两个文件: app.tswikipedia-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)

我们打开了Promise的结果,即 WikipediaService 的搜索方法返回并将其作为一个简单的字符串数组暴露给模板,以便我们可以通过它*ngFor循环*ngFor并为我们构建一个列表。

请参阅Plunker基于 Promise 的实现示例


Observables真正发光的地方

让我们改变我们的代码,不要在每次击键时敲击端点,而只是在用户停止键入400 ms时才发送请求

为了揭示这样的超能力,我们首先需要获得一个带有用户输入的搜索词的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> ,当没有 400ms 的新值时,它将只发出一个新值。

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操作符

请参阅Plunker上的Observable实现示例

要处理无序响应,请查看完整的文章http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

至于我在 Angular 中使用 Http,我同意在正常使用情况下使用 Observable 而不是 Promise 没有太大区别。在实践中,这些优点都没有真正相关。希望将来可以看到一些高级用例:)


学到更多

PromisesObservable都将帮助我们使用 JavaScript 中的异步功能 。它们在许多情况下非常相似,但是,两者之间仍然存在一些差异,承诺是将以asynchronous方式(如http调用)解析的值。另一方面,observable 处理一系列异步事件 。它们之间的主要区别如下:

诺言:

  • 有一个管道
  • 通常只用于异步数据返回
  • 不容易取消

观察到:

  • 是可以取消的
  • 当然可以重试,例如重试和重试
  • 在多个管道中传输数据
  • 具有类似于数组的操作,如 map,filter 等
  • 可以从事件等其他来源创建
  • 它们是函数,可以在以后订阅

另外,我在下面为您创建了图形图像,以便直观地显示差异:

承诺和观察者形象