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

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


当前回答

简短回答:

可观察性更好。它具有所有Promise功能和额外功能。


长答案:

承诺:

一次性使用“返回数据一次”不取消一位听众无插座支持

观察结果:

数据更改时多次返回数据支持取消支撑插座支持许多侦听器,并在数据更改时通知它们支持映射、过滤和减少

其他回答

虽然Günter Zöchbauer的回答总体上是好的,但我不认为它强调了在处理角度分量时,您几乎总是希望使用Observable,因为它支持取消。承诺不能取消,即使您的组件被破坏,承诺也会得到解决。Angular倾向于宽容,直到不宽容为止。

例如,对损坏组件的任何手动更改检测都会导致异常:

ngOnInit() {
  // Promise API
  this.service.getData().then(d => {
     this.data = d;
     this.changeDetectorRef.detectChanges();
  });

  // Observable API
  this.service.getData().pipe(takeUntil(this.unsubscribe)).subscribe((d) => {
     this.data = d;
     this.changeDetectorRef.detectChanges();
  });
}

如果您的组件在promise解析之前被销毁,那么在promise被解析时,您将尝试使用已销毁的视图错误。

或者,如果您在takeUntil模式中使用可观测值,那么一旦您的组件被破坏,订阅就会被取消。

这是一个有点做作的例子,但为被破坏的组件执行代码可能会导致错误。

许诺

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

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

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

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

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

承诺:

提供单一的未来价值;不懒惰;不可撤销;

可观察:

随时间推移发出多个值;懒惰的可取消;支持map、filter、reduce和类似运算符

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