Promises 和 Observables 有什么区别?

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

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

答案

诺言

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

注意:那里有支持取消的Promise库,但是 ES6 Promise到目前为止还不支持。

可观察的

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

通常, ObservablePromise更为可取,因为它提供了Promise等功能。使用Observable ,您要处理 0、1 或多个事件都没有关系。在每种情况下,您都可以使用相同的 API。

Promise相比, Observable也具有可取消的优势。如果不再需要对服务器的 HTTP 请求或其他昂贵的异步操作的结果,则ObservableSubscription可以取消该预订,而Promise最终将调用成功或失败的回调,即使您不需要需要通知或它提供的结果了。

Observable 提供类似于数组的mapforEachreduce ,... 等运算符

还有一些功能强大的运算符,例如retry()replay() …… 通常非常方便。

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

由于一个代码片段包含一千个单词,因此下面的示例使它们更容易理解。

感谢 @Christoph Burgdorf 的精彩文章


Angular 使用 Rx.js Observables 代替了用于处理 HTTP 的 Promise。

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

  • 我们不想每次用户按下一个键时都击中服务器端点,它应该向他们发出大量HTTP请求。基本上,我们只希望在用户停止键入后才敲击它,而不是每次击键都击中它。
  • 不要为后续请求使用具有相同查询参数的搜索端点。
  • 处理乱序响应。当我们同时有多个请求进行时,我们必须考虑到它们以意外顺序返回的情况。想象一下,我们先键入计算机 ,停止,一个请求熄灭,我们键入汽车 ,停止,一个请求熄灭。现在,我们有两个正在进行的请求。不幸的是,载有计算机结果的请求在载有汽车结果的请求之后又回来了。

该演示将仅包含两个文件: 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循环并为我们建立一个列表。

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


可观察的事物真正发光的地方

让我们更改代码,以免每次敲击都敲击端点,而仅在用户停止键入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自动将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));
  }
}

发送另一个对我们的应用程序已经显示出搜索结果的搜索词的请求,将浪费资源。所有我们需要做的,以实现所需的行为是调用distinctUntilChanged操作正确的,我们叫做后debounceTime(400)

请参阅Plunker上的Observable实现示例

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

就我在 Angular 中使用 Http 而言,我同意在正常使用情况下使用 Observable over Promise 并没有太大区别。在实践中,这些优势都没有真正相关。希望以后能看到一些高级用例:)


学到更多

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

诺言:

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

可观察的:

  • 可以取消
  • 本质上可以重试,例如重试和重试时间
  • 在多个管道中传输数据
  • 具有类似数组的操作,例如地图,过滤器等
  • 可以从事件等其他来源创建
  • 它们是函数,以后可以订阅

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

承诺和可观察的图像

承诺

  1. 定义:帮助您异步运行函数,并使用它们的返回值(或异常),但在执行时仅使用一次
  2. 不偷懒
  3. 不可取消(那里有 Promise 库支持取消,但是 ES6 Promise 到目前为止还不支持)。两个可能的决定是
    • 拒绝
    • 解决
  4. 无法重试 (承诺应有权访问返回了具有重试功能的诺言的原始函数,这是一种不好的做法)

可观察的

  1. 定义:帮助您异步运行函数,并在执行时以连续顺序( 多次 )使用它们的返回值。
  2. 默认情况下,它是惰性的,因为随着时间的推移它会发出值。
  3. 有很多运算符,简化了编码工作。
  4. 可以在需要时使用一个运算符重试来重试,如果需要根据某些条件重试可观察的对象,也可以使用retryWhen

    注意 :可以在RxMarbles.com 上找到操作员列表及其交互图。

答案中缺少 Observables 的缺点。承诺允许使用 ES7 异步 / 等待功能。使用它们,您可以编写异步代码,就像执行同步函数调用一样,因此您不再需要回调。 Observables 这样做的唯一可能性是将它们转换为 Promises。但是,当您将它们转换为 Promises 时,您只能再次获得一个返回值:

async function getData(){
    const data = await observable.first().toPromise();
    //do stuff with 'data' (no callback function needed)
}

进一步阅读: 如何在 Rx Observable 上等待?

Promises 和 Observables 都仅处理异步调用。

这是它们之间的区别:

可观察的

  1. 在一段时间内发出多个值
  2. 在我们订阅 Observable 之前不会调用
  3. 可以通过使用 unsubscribe()方法取消
  4. 提供映射,用于运算符的每次,过滤,缩小,重试和重试

诺言

  1. 一次仅发出一个值

  2. 调用不带. then 和. catch 的服务

  3. 无法取消

  4. 不提供任何运算符

即使这个答案很晚,我还是总结了以下差异,

可观察的:

  1. Observable 只是一个function ,它an observer并返回一个function Observer: an object with next, error.
  2. 观察员允许subscribe/unsubscribe其数据流,发出下一个值观察者, notify有关观察者errors ,并告知观察者对stream completion
  3. Observer 提供了一个function to handle next value ,错误和流结束(ui 事件,http 响应,带有 Web 套接字的数据)的功能。
  4. 随着时间的推移使用multiple values
  5. 它是cancel-able/retry-able并支持诸如map,filter,reduce等运算符。
  6. 创建一个 Observable 可以是Observable.create() - 返回可以调用方法的Observer Observable.from() - Observer Observable.from() - 将数组或可迭代对象转换为Observable Observable.fromEvent() - 将事件转换为 Observable- Observable.fromPromise() - 将 Promise 转换为 Observable- Observable.range() - 返回指定范围内的整数序列

承诺

  1. 许诺代表了一项将在未来完成的任务;

  2. 承诺resolved by a value ;

  3. 承诺被例外拒绝;

  4. 不可cancellable ,它返回a single value

  5. 一个承诺暴露一个功能(then)

    - 然后返回新的promise

    - 允许根据state执行attachment

    - guaranteed handlers order attached执行;

我刚刚处理了一个问题,其中 “承诺” 是最好的解决方案,我在这里与任何共享它的人分享,以免遇到任何有用的问题(这正是我之前所寻找的答案):

在 Angular2 项目中,我有一个接受一些参数并返回值列表以填充表单上下拉菜单的服务。表单组件初始化时,我需要使用不同的参数多次调用同一服务,以定义多个不同的下拉菜单,但是,如果我只是将所有变量排队,以调用该服务,则只有最后一个成功,其余错误出来。从数据库中获取的服务一次只能处理一个请求。

成功填充所有下拉菜单变量的唯一方法是调用服务,以防止在最后一个请求完成之前处理新请求,并且 Promise / .then 机制很好地解决了该问题。

fetchValueList(listCode): Promise<any> {
      return this.dataSvc.getValueList(listCode, this.stateSvc.currentContext, this.stateSvc.currentLanguageCode)
          .map(response => response.json())
          .toPromise();
  }

  initializeDropDowns() {
      this.fetchValueList('First-Val-List')
          .then(data => {
              this.firstValList = data;
              return this.fetchValueList('Second-Val-List')
          }).then(data => {
              this.secondValList = data;
              return this.fetchValueList('Third-Val-List')
          }).then(data => {
              this.thirdValList = data;
          })  }

我在组件中定义了函数,然后在 ngOnInit 中调用了 initializeDropDowns()。

fetchValueList 函数返回一个 Promise,因此第一个调用传递第一个 listCode,当 Promise 解析时,返回值在. then 块的 data 变量中,我们可以将其分配给 this.firstValList 变量。由于函数已返回数据,我们知道服务已完成,可以安全地使用第二个 listCode 再次调用,返回值位于下一个. then 块的 data 变量中,并将其分配给 this.secondValList 变量。

我们可以根据需要将其链接多次,以填充所有变量,并且在最后一个代码块中,我们只需省略 return 语句,然后该块终止。

这是一个非常特殊的用例,其中我们需要在组件初始化时多次调用一个服务,并且该服务必须完成获取并返回值才能再次调用它,但是在这种情况下, Promise / .then 方法是理想的。

我相信所有其他答案都应该清除您的疑问。尽管如此,我只是想补充一点,可观察对象是基于函数式编程的,并且我发现它所附带的函数非常有用,例如 map,flatmap,reduce,zip。 Web 实现的一致性,特别是当它依赖于 API 请求时,是一种残酷的改进。

我强烈推荐该文档 ,因为它是 reactX 的正式文档,并且我认为它是最清晰的文档。

如果您想进入可观察的领域,我建议这篇文章分为三部分: http : //blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

尽管它是用于 RxJava 的,但概念是相同的,并且确实有很好的解释。在 reactX 文档中,您具有每个功能的等效项。您必须寻找 RxJS。

诺言:

  • 提供单一的未来价值;
  • 不偷懒;
  • 无法取消;

可观察的:

  • 随着时间的推移发出多个值;
  • 懒;
  • 可取消;
  • 支持映射,过滤,缩小和类似运算符

如果愿意,可以在 Angular 中调用 HTTP 时使用 Promise 而不是 Observables。