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

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


当前回答

Observables和Promise帮助我们使用JavaScript/TypeScript中的异步功能。它们在许多情况下非常相似,但它们之间仍有一些差异。

其他回答

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在异步操作完成或失败时处理单个事件。

注意:有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对象表示异步操作的最终完成(或失败)及其结果值。

语法:new Promise(executor);

例如:

var promise_eg = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('foo');
  }, 300);
});

promise_eg.then(function(value) {
  console.log(value);
  // expected output: "foo"
});

console.log(promise_eg);

关于Promise:

它有一个管道,因此在调用时只返回一次值。它是一个单向处理程序,因此一旦调用,您可能无法取消。可以在when()和then()之间使用有用的语法。

观察结果:

可观测值是一段时间内多个值的惰性集合。这是一种非常好的异步操作方法。它可以用具有跨平台支持的rxjs实现,也可以与Angular/React等一起使用。

它的作用类似于流衬垫,可以是多管道。因此,一旦定义,您可以在许多地方订阅以获得返回结果。

语法:从“@reactivex/rxjs”导入*作为Rx;初始化:

Rx.Observable.fromEvent(button, "click"),
Rx.Subject()

etc.

订阅:RxLogger.getInstance();

例如:

import { range } from 'rxjs';
import { map, filter } from 'rxjs/operators';

range(1, 200).pipe(
  filter(x => x % 2 === 1),
  map(x => x + x)
).subscribe(x => console.log(x));

由于它支持多管道,您可以在不同的位置订阅结果,

它有比承诺更多的可能性。

用法:

它有更多的可能性,如贴图、过滤器、管道、贴图、concatMap等。

Promise在异步活动完成或失败时发出单个事件。

Observable类似于流(在许多语言中),允许传递至少零个或多个事件,其中每个事件都需要回调。

与Promise相比,Observable通常更受欢迎,因为它提供了Promise等的亮点。使用Observable,您是否需要处理0、1或各种事件并不重要。您可以针对每种情况使用类似的API。

承诺:promise只产生一种价值

例如:

const numberPromise = new Promise((resolve) => {
    resolve(5);
    resolve(10);
});

numberPromise.then(value => console.log(value));
// still prints only 5

可观察:在一段时间内发出多个值

例如:

  const numberObservable = new Observable((observer) => {
        observer.next(5);
        observer.next(10);
    });

numberObservable.subscribe(value => console.log(value));
// prints 5 and 10

我们可以想象一个可观察的流,它在一段时间内发出多个值,对发出的每个项调用相同的回调函数。无论该数据是作为单个值还是作为多个值在某段时间内传输。

承诺:

承诺不是懒惰承诺不能取消

可观察:

可观察就是懒惰。“可观察”是缓慢的。直到我们订阅了它。可以使用unsubscribe()方法取消Observable另外,Observable提供了许多强大的运算符,foreach、filter、reduce、retry、retryWhen等。

角度承诺与可观测

概述:

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被订阅时,它将调用传入订阅的相应回调。