关于Angular:Promises 和Observables有什么区别?

What is the difference between Promises and Observables?

有人能解释一下PromiseObservable在角度上的区别吗?

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


许诺

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

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

可观察的

Observable类似于Stream(多种语言),允许传递零个或多个事件,其中对每个事件调用回调。

通常,ObservablePromise更受欢迎,因为它提供了Promise和更多的特性。对于Observable来说,是否要处理0、1或多个事件并不重要。您可以在每种情况下使用相同的API。

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

Observable提供了诸如mapforEachreduce…。类似于数组

还有一些强大的运营商,如retry()replay()……这通常很方便。


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

因为代码片段值一千个单词,所以让我们通过下面的示例更容易理解它们。好的。

Thanks @Christoph Burgdorf for the awesome article

Ok.

Angular使用RX.JS观测数据,而不是承诺处理HTTP。好的。

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

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

演示只包含两个文件:app.tswikipedia-service.ts。不过,在现实世界中,我们很可能会把事情进一步分开。好的。

下面是基于承诺的实现,它不处理任何描述的边缘情况。好的。

wikipedia-service.ts好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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服务,以使用给定的搜索词对维基百科API提出GET请求。注意,我们调用toPromise是为了从ObservablePromise。最终以Promise>作为搜索方法的返回类型结束。好的。

app.ts好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// check the plnkr for the full list of imports
import {...} from '...';

@Component({
  selector: 'my-app',
  template: `
   
      Wikipedia Search
      <input #term type="text" (keyup)="search(term.value)">
     
<ul>

        <li *ngFor="let item of items">{{item}}
</li>

     
</ul>

   
  `
})
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)。好的。

我们解开了wikipedia服务的搜索方法返回的承诺的结果,并将其作为一个简单的字符串数组公开给模板,这样我们就可以让*ngFor循环它,并为我们建立一个列表。好的。

参见基于Promise的Pluker实现示例好的。

可见光真正闪耀的地方好的。

让我们将代码更改为不在每次按键时敲打端点,而是只在用户停止键入400毫秒时发送请求。好的。

为了展示这种超能力,我们首先需要得到一个带有用户输入搜索词的Observable。我们可以利用Angular的formControl指令,而不是手动绑定到keyup事件。要使用这个指令,我们首先需要将ReactiveFormsModule导入我们的应用程序模块。好的。

app.ts好的。

1
2
3
4
5
6
7
8
9
10
11
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"。好的。

1
<input type="text" [formControl]="term"/>

在我们的组件中,我们从@angular/form创建了formControl的实例,并将其作为一个字段暴露在组件的名称项下。好的。

在幕后,术语会自动公开一个Observable作为我们可以订阅的valueChanges属性。现在我们有了一个Observable,克服用户输入就和在Observable上调用debounceTime(400)一样容易。这将返回一个新的Observable,只有在400毫秒内没有新值时才会发出新值。好的。

1
2
3
4
5
6
7
8
9
10
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操作符。好的。

参见Punker上的可观察实现示例好的。

For dealing with out-of-order responses, please check the full article
http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

Ok.

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

Learn more

Ok.

  • https://angular-2-training-book.rangle.io/handout/observables/
  • https://angular.io/tutorial/toh-pt6#observables

好啊。


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

承诺:

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

可观察到的:

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

此外,我还为您创建了下面的图形图像,以直观地显示不同之处:

Promises and Observables image


承诺

  • 定义:帮助您异步运行函数,并使用它们的返回值(或异常),但在执行时只使用一次。
  • 不懒
  • 不可取消。两个可能的决定是
    • 拒绝
    • 决心
  • 无法重试(Promises应该可以访问返回Promises的原始函数,以便具有重试功能,这是一种糟糕的做法)
  • 可观测的

  • 定义:帮助您异步运行函数,并在执行时以连续序列(多次)使用它们的返回值。
  • 默认情况下,它是懒惰的,因为它会在时间推移时发出值。
  • 有很多简化编码工作的运算符。
  • 当需要时,可以使用一个操作员重试来重试,如果我们需要根据某些条件重试可观察到的,也可以使用重试。

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


  • 答案中有一个缺点是看得见的东西丢失了。承诺允许使用ES7异步/等待函数。使用它们,您可以编写异步代码,就像它是一个同步函数调用一样,因此您不再需要回调。唯一的可能性,可观察到这样做,是把它们转化为承诺。但当你将它们转化为承诺时,你只能再得到一个返回值:

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

    进一步阅读:我如何"等待"接收到的?


     Promises vs Observables

    promises和observable都只处理异步调用。主要区别的图像。


    尽管这个答案很晚,但我总结了以下的差异:

    Observable:

  • observable只是一个function,它接受an observer并返回function Observer: an object with next, error.
  • observer允许subscribe/unsubscribe到它的数据流,发出观察者的下一个值,notify观察者关于errors和通知观察员关于stream completion
  • 观察者提供了一个function to handle next value,错误和流结尾(UI事件、HTTP响应、带Web套接字的数据)。
  • 随时间推移与multiple values合作
  • cancel-able/retry-able,支持map,filter,reduce等操作。
  • 创建一个可观察的-Observable.create()—返回可观察的,可以在-Observer Observable.from()—将数组或iterable转换为-Observable Observable.fromEvent()—将事件转换为可观察的-Observable.fromPromise()—将承诺转化为可观察的承诺。-Observable.range()—返回指定的整数序列?埃德山脉
  • 承诺:

  • 承诺代表将来要完成的任务;

  • 承诺变成了承诺;

  • 承诺被例外情况拒绝;

  • 不是cancellable,返回a single value

  • 承诺暴露了一种功能

    -然后返回一个新的promise

    -允许其中的attachment将根据执行state

    -handlersguaranteedorder attached执行;


  • 我相信所有其他的答案都能消除你的疑虑。不过,我只是想补充一下,观测数据是基于函数编程的,我发现它附带的函数非常有用,比如map、flatmap、reduce、zip。Web实现的一致性,特别是当它依赖于API请求时,是一个残酷的改进。

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

    如果你想进入观察,我建议这3部分的帖子:http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

    虽然它是为RXJava设计的,但是概念是相同的,并且它被很好地解释了。在reactivex文档中,您拥有每个函数的等价物。你必须寻找RXJ。


    我刚处理过一个问题,承诺是最好的解决方案,我将在这里为任何在这个问题有用时遇到困难的人分享它(这正是我之前寻找的答案):

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      fetchValueList(listCode): Promise {
          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解析时,返回值位于中的数据变量中。然后在块中我们可以将其分配给this.firstvallist变量。由于函数返回了数据,我们知道服务已经完成,使用第二个listcode再次调用是安全的,返回值在下一个的数据变量中。然后阻塞并将其分配给this.secondvallist变量。

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

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


    承诺:

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

    Observable:

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

    如果愿意的话,在以角度调用HTTP时,可以使用承诺而不是可见项。


    概述:

    • 承诺和观察都有助于我们处理异步操作。当这些异步操作完成时,它们可以调用某些回调。
    • 一个承诺只能处理一个事件,可观察的是一段时间内的事件流。
    • 承诺一经兑现就不能取消
    • 可观测数据的发射可以使用运算符进行转换

    您总是可以使用Observable来处理异步行为,因为Observable具有Promise提供的所有功能(+Extra)。但是,有时不需要Observables提供的额外功能。然后,为它导入一个库以便使用它们将是额外的开销。

    何时使用承诺:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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状态被解析时,调用then()方法。当承诺状态被拒绝时,调用catch()方法。

    何时使用观测设备:

    当一段时间内有一个(数据流)需要处理时,使用观察数据。流是一系列数据元素,这些元素随着时间的推移而可用。流的例子有:

  • 用户事件,例如单击或键控事件。用户会随着时间的推移生成事件(数据)。
  • WebSockets,在客户端与服务器建立WebSocket连接之后它会随着时间推移推送数据。
  • 在Observable中,它本身是在下一个事件发生时、错误发生时或Observable完成时指定的。然后我们可以订阅这个Observable,它会激活它,在这个订阅中,我们可以传入3个回调(不必总是全部传入)。为成功执行一个回调,为错误执行一个回调,为完成执行一个回调。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    当创建一个Observable时,它需要一个回调函数,该函数提供一个Observator作为参数。在这个观察者中,你可以称为onNextonCompletedonError。然后,当Observable被订阅时,它将调用传递到订阅中的相应回调。


    承诺-提供单一的未来价值。不懒惰。不能取消。它要么拒绝要么解决。

    可观察-提供多种未来价值。懒惰的可以取消。它还提供了其他方法:实时地图、过滤、缩小。


    观察和承诺都提供了一种在JavaScript中处理异步活动的方法。虽然承诺根据单个异步事件(HTTP请求)的完成来拒绝/解决,但是Observables可以根据订阅它们的观察者持续地发出状态更改。

    它们之间的一个基本区别是,Observable提供了取消请求和重新发送新请求的方法。承诺不允许这样的功能。

    同样,Promise发出一个值,而Observable发出多个值。因此,在处理HTTP请求时,Promise可以为同一请求管理单个响应,但是如果对同一请求有多个响应,那么我们必须使用Observable。


    Below are some important differences in promises & Observables.

    许诺

    • 仅发出一个值
    • 不可取消
    • 不可共享
    • 总是异步的

    可观察的

    • 发出多个值
    • 仅在调用或有人订阅时执行
    • 可以取消
    • 可以由多个订阅服务器共享和订阅该共享值。所有订户将在一个时间点执行。
    • 可能是异步的

    要了解更多信息,请参阅https://stackblitz.com/edit/observable-vs-promises


    可观察和承诺的基本区别是:

    enter image description here


    简短回答:

    可观察性更好,它有所有的承诺功能和额外的功能。

    长回答:

    承诺:

    • 一次性使用"返回数据一次"
    • 不取消
    • 一个听众
    • 没有套接字支持一个侦听器

    Observable:

    • 数据更改时多次返回数据
    • 支持取消
    • 支撑插座
    • 支持许多侦听器,并在数据更改时通知它们
    • 支持地图、过滤、缩小


    虽然公认的答案一般来说是好的,但我不认为它强调了当处理角分量时,你几乎总是希望使用一个可观测的,因为它支持取消。承诺不能取消,即使您的组件被破坏,承诺也会解决。角度往往是宽容的,直到它不是。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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();
      });
    }

    如果您的组件在承诺得到解决之前被销毁,那么在承诺得到解决时,您将得到一个attempt to use destroyed view错误。

    或者,如果您使用takeuntil模式的Observables,那么一旦您的组件被破坏,订阅将被取消。

    这是一个有点做作的例子,但是为被破坏的组件执行代码可能会导致错误。除非你真的出于某种原因想这么做:p


    观察和承诺帮助我们使用javascript/typescript中的异步功能。它们在许多情况下都非常相似,但是它们之间仍然存在一些差异。

    enter image description here


    在第一次阅读教程和文档时,我遇到了一个不明显的问题,那就是多播的想法。

    确保您知道,默认情况下,多个订阅将在一个可观察的表中触发多个执行。对单个HTTP调用Observable的多个订阅将触发多个相同的HTTP调用,除非您.share()(启用多播)。

    承诺迫使您一次只处理一件事情,打开它的数据,处理异常,对诸如async/await之类的酷事情有语言支持,否则就相当简单了。

    一个观测仪有很多钟和哨子,但你需要了解你正在工作的力量,否则它可能被滥用。


    我看到很多人使用"可观测"这个论点,认为"可观测"是"可取消的",但承诺"可取消"是相当微不足道的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function cancellablePromise(body) {
      let resolve, reject;
      const promise = new Promise((res, rej) => {
        resolve = res; reject = rej;
        body(resolve, reject)
      })
      promise.resolve = resolve;
      promise.reject = reject;
      return promise
    }

    // Example 1: Reject a promise prematurely
    const p1 = cancellablePromise((resolve, reject) => {
      setTimeout(() => resolve('10', 100))
    })

    p1.then(value => alert(value)).catch(err => console.error(err))
    p1.reject(new Error('denied')) // expect an error in the console

    // Example: Resolve a promise prematurely
    const p2 = cancellablePromise((resolve, reject) => {
      setTimeout(() => resolve('blop'), 100)
    })

    p2.then(value => alert(value)).catch(err => console.error(err))
    p2.resolve(200) // expect an alert with 200


    承诺:

    异步事件处理程序-Promise对象表示异步操作的最终完成(或失败)及其结果值。

    语法:新承诺(执行人);

    如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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);

    enter image description here

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

    Observables:

    观察数据是一段时间内多个值的惰性集合。这是一种非常好的异步操作方法。它可以与具有跨平台支持的RXJS一起完成,可以与角度/反应等一起使用。

    它的行为像一条小溪。可以是多管道。所以一旦定义好,您就可以订阅在许多地方获得返回结果。

    语法:import * as Rx from"@reactivex/rxjs";init:

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

    认购:RxLogger.getInstance();

    如:

    1
    2
    3
    4
    5
    6
    7
    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));

    因为它支持多个管道,所以您可以在不同的位置订阅结果,enter image description here它比承诺有更多的可能性。

    用途:它有更多的可能性,如map, filter, pipe, map, concatMap etc


    关于这个话题已经有很多答案了,所以我不会再增加一个多余的答案。

    但是对于那些刚开始学习可观察/角度和怀疑哪一个与承诺相比使用的人,我建议你保持所有可观察的东西,并将你项目中的所有现有承诺转化为可观察的。

    仅仅因为角框架本身和它的社区都是使用可观察的。因此,当您集成框架服务或第三方模块并将所有内容链接在一起时,这将是有益的。

    虽然我很欣赏所有的落选,但我仍然坚持以上我的观点,除非有人给出适当的评论,列出一些可能在你的角度项目中仍然有用的场景,以使用承诺而不是可观察的。

    当然,在所有情况下,没有一个观点是100%正确的,但至少我认为98%的时间是在角度框架中执行常规商业项目的,可观察是正确的方式。

    即使你不喜欢在你的简单爱好项目的起点,你很快会意识到几乎所有的组件,你互动的角度,和大多数角度友好的第三方框架使用可观察,然后你会不断转换你的承诺,以可观察,以便与他们沟通。

    这些组件包括但不限于:HTTPClient、Form Builder、Angular Material模块/对话框、NGRX Store/Effects和NGX引导。

    事实上,我在过去2年里处理过的角度生态系统的唯一承诺就是APP_INITIALIZER