在Angular中Promise和Observable之间有什么区别?

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


当前回答

我相信所有其他的答案都会消除你的疑虑。然而,我只是想补充一点,可观测性是基于函数式编程的,我发现它附带的函数非常有用,比如map、flatmap、reduce、zip。web实现的一致性,尤其是当它依赖于API请求时,是一种残酷的改进。

我强烈推荐这个文档,因为它是reactiveX的官方文档,我发现它是最清晰的。

如果你想了解可观测性,我建议你写一篇三部分的文章:http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

虽然它是针对RxJava的,但概念是相同的,而且解释得很好。在reactiveX文档中,您拥有每个函数的等效项。您必须查找RxJS。

其他回答

承诺是渴望的,而可观察者是懒惰的。Promise总是异步的,而Observable可以是同步或异步。Promise可以提供单个值,而Observable是值流(从0到多个值)。您可以将RxJS运算符应用于Observable,以获得新的定制流动

假设你想去海滩。你必须根据天气做出决定。您有三种方法:

你往外面看,看到雨点,所以你改变了主意。这是一个同步操作。你停止了你正在做的事情,去外面看看,得到了结果,然后回到你正在做什么。你请你旁边的哥哥检查一下今天的天气状况。当他在检查天气时,你仍然在做你正在做的事情。这是一个异步操作。你把一项任务交给了你的兄弟,等待承诺得到解决。在这种情况下,您将得到一个响应,在得到响应后,您将不再得到任何更新。这一次,你打开收音机,收听全天候播报天气状况的天气频道。在这种情况下,响应是持续的,而不是得到一个响应。此响应类似于订阅可观察的。可观测的是“天气”,订阅的是“让你不断更新的无线电信号”。只要你的收音机开着,你就会得到所有可用的更新。在你关掉收音机之前,你不会错过任何信息。当你关闭收音机时,意味着“你已取消订阅”。

许诺

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

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

可观察的

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

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

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

当Promise立即启动时,Observable只有在您订阅它时才会启动。这就是为什么Observable被称为lazy。

Observable提供了map、forEach、reduce等运算符。。。类似于阵列

还有一些强大的运算符,如retry()或replay()。。。这通常很方便。rxjs附带的操作员列表

惰性执行允许在通过订阅执行可观察到的操作之前建立一个运算符链,以进行更具声明性的编程。

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

由于一段代码相当于一千个单词,所以让我们通过下面的示例来更容易地理解它们。

感谢@Christoph Burgdorf的精彩文章


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

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

我们不希望每次用户按下键时都会触及服务器端点。它应该会向他们发出大量HTTP请求。基本上,我们只想在用户停止打字后点击它,而不是每次按键。对于后续请求,不要使用相同的查询参数到达搜索端点。处理无序响应。当我们同时处理多个请求时,我们必须考虑这些请求以意外顺序返回的情况。想象一下,我们首先键入计算机,停止,一个请求发出,我们键入汽车,停止,请求发出。现在我们有两个请求正在执行中。不幸的是,携带计算机结果的请求在携带汽车结果的请求之后返回。

演示将仅由两个文件组成:app.ts和wikipedia-service.ts。然而,在现实世界场景中,我们很可能将事情进一步拆分。


下面是一个基于Promise的实现,它不处理任何描述的边缘情况。

维基百科服务.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是为了从一个可观察的<Response>转换为Promise<Response>。最终以Promise<Array<string>作为搜索方法的返回类型。

应用程序

// 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的实现示例


观察者真正闪耀的地方

让我们将代码更改为不在每次击键时敲击端点,而是仅在用户停止键入400ms时发送请求

要揭示这些超级功能,我们首先需要获得一个Observable<string>,它包含用户键入的搜索项。我们可以利用Angular的formControl指令,而不是手动绑定到keyup事件。要使用此指令,我们首先需要将ReactiveFormsModule导入到应用程序模块中。

应用程序

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实例,并将其作为组件名称项下的字段公开。

在幕后,术语会自动将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 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 over Promise没有太大的区别。在实践中,没有一个优点是真正相关的。我希望将来能看到一些高级用例:)


了解更多信息https://angular-2-training-book.rangle.io/handout/observables/https://angular.io/tutorial/toh-pt6#observables

概述:

Promise和Observables都帮助我们处理异步操作。当这些异步操作完成时,它们可以调用某些回调。Promise只能处理一个事件,Observables用于一段时间内的事件流承诺一旦待定,就不能取消可以使用运算符转换可观测数据发射

您可以始终使用可观察对象来处理异步行为,因为可观察对象具有承诺提供的所有功能(+额外功能)。然而,有时不需要Observables提供的额外功能。然后,导入一个库以供其使用将是额外的开销。

何时使用Promise:

当您有一个要处理结果的异步操作时,请使用promise。例如:

var promise = new Promise((resolve, reject) => {
  // do something once, possibly async
  // code inside the Promise constructor callback is getting executed synchronously

  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

//after the promise is resolved or rejected we can call .then or .catch method on it

promise.then((val) => console.log(val))      // logs the resolve argument
       .catch((val) => console.log(val));    // logs the reject argument

因此,promise执行一些代码,它要么解析,要么拒绝。如果称为resolve或reject,则promise将从挂起状态变为resolved或reject状态。当promise状态被解析时,将调用then()方法。当promise状态被拒绝时,将调用catch()方法。

何时使用Observables:

当一段时间内存在需要处理的数据流时,使用Observables。流是一系列随时间而可用的数据元素。流的示例有:

用户事件,例如单击或键控事件。用户随时间生成事件(数据)。Websockets,在客户端与服务器建立WebSocket连接后,它会随时间推移推送数据。

在Observable中,指定了下一个事件发生的时间、错误发生的时间或Observable完成的时间。然后,我们可以订阅这个可观察的,它激活了它,在这个订阅中,我们可以传递3个回调(不必总是传递所有回调)。成功时执行一次回调,错误时回调,完成时回调。例如:

const observable = Rx.Observable.create(observer => {
  // create a single value and complete
  observer.onNext(1);
  observer.onCompleted();
});

source.subscribe(
  x => console.log('onNext: %s', x),   //  success callback
  e => console.log('onError: %s', e),  //  error callback
  () => console.log('onCompleted')     //  completion callback
 );

// first we log: onNext: 1
//  then we log: onCompleted

当创建一个可观察对象时,它需要一个回调函数,该函数提供一个观察者作为参数。在这个观察者上,您可以调用onNext、onCompleted和onError。然后,当Observable被订阅时,它将调用传入订阅的相应回调。