What is the difference between Promises and Observables?
有人能解释一下
每个例子都有助于理解这两种情况。在什么情况下我们可以使用每个案例?
许诺
当异步操作完成或失败时,
注:目前有支持取消的
可观察的
通常,
Observable提供了诸如
还有一些强大的运营商,如
因为代码片段值一千个单词,所以让我们通过下面的示例更容易理解它们。好的。
Thanks @Christoph Burgdorf for the awesome article
Ok.
Angular使用RX.JS观测数据,而不是承诺处理HTTP。好的。
假设您正在构建一个搜索函数,该函数应在键入时立即显示结果。听起来很熟悉,但这项任务带来了很多挑战。好的。
- 我们不希望每次用户按下一个键时都击中服务器端点,它应该会向他们发送大量的
HTTP 请求。基本上,我们只想在用户停止输入时点击它,而不是每次按键。 - 对于后续请求,不要使用相同的查询参数访问搜索端点。
- 处理无序响应。当我们在飞行中同时有多个请求时,我们必须考虑这些请求以意外的顺序返回的情况。想象一下,我们先打电脑,停下来,一个请求发出,我们打汽车,停下来,一个请求发出。现在我们在飞行中有两个请求。不幸的是,承载计算机结果的请求会在承载汽车结果的请求之后返回。
演示只包含两个文件:
下面是基于承诺的实现,它不处理任何描述的边缘情况。好的。
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]); } } |
我们正在注入
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); } } |
这里也没什么惊喜。我们注入我们的
我们解开了wikipedia服务的搜索方法返回的承诺的结果,并将其作为一个简单的字符串数组公开给模板,这样我们就可以让
参见基于Promise的Pluker实现示例好的。
可见光真正闪耀的地方好的。
让我们将代码更改为不在每次按键时敲打端点,而是只在用户停止键入400毫秒时发送请求。好的。
为了展示这种超能力,我们首先需要得到一个带有用户输入搜索词的
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"/> |
在我们的组件中,我们从
在幕后,术语会自动公开一个
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)); } } |
发送另一个搜索词的请求,我们的应用程序已经显示了搜索结果,这将浪费资源。为了达到预期的行为,我们所要做的就是在调用
参见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.htmlOk.
就我在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中的异步功能。它们在许多情况下非常相似,但是,两者之间仍然存在一些差异,承诺是在
承诺:
- 有一条管道
- 通常只用于异步数据返回
- 不容易取消
可观察到的:
- 可取消的
- 根据性质是否可重试,如重试和重试
- 在多个管道中传输数据
- 具有类似数组的操作,如map、filter等
- 可以从事件等其他源创建
- 它们是函数,稍后可以订阅
此外,我还为您创建了下面的图形图像,以直观地显示不同之处:
承诺
- 拒绝
- 决心
可观测的
当需要时,可以使用一个操作员重试来重试,如果我们需要根据某些条件重试可观察到的,也可以使用重试。
注意:可以在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和observable都只处理异步调用。主要区别的图像。
尽管这个答案很晚,但我总结了以下的差异:
Observable:
承诺:
承诺代表将来要完成的任务;
承诺变成了承诺;
承诺被例外情况拒绝;
不是
承诺暴露了一种功能
-然后返回一个新的
-允许其中的
-
我相信所有其他的答案都能消除你的疑虑。不过,我只是想补充一下,观测数据是基于函数编程的,我发现它附带的函数非常有用,比如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状态被解析时,调用
当一段时间内有一个(数据流)需要处理时,使用观察数据。流是一系列数据元素,这些元素随着时间的推移而可用。流的例子有:
在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作为参数。在这个观察者中,你可以称为
承诺-提供单一的未来价值。不懒惰。不能取消。它要么拒绝要么解决。
可观察-提供多种未来价值。懒惰的可以取消。它还提供了其他方法:实时地图、过滤、缩小。
观察和承诺都提供了一种在JavaScript中处理异步活动的方法。虽然承诺根据单个异步事件(HTTP请求)的完成来拒绝/解决,但是Observables可以根据订阅它们的观察者持续地发出状态更改。
它们之间的一个基本区别是,Observable提供了取消请求和重新发送新请求的方法。承诺不允许这样的功能。
同样,Promise发出一个值,而Observable发出多个值。因此,在处理HTTP请求时,Promise可以为同一请求管理单个响应,但是如果对同一请求有多个响应,那么我们必须使用Observable。
Below are some important differences in promises & Observables.
许诺
- 仅发出一个值
- 不可取消
- 不可共享
- 总是异步的
可观察的
- 发出多个值
- 仅在调用或有人订阅时执行
- 可以取消
- 可以由多个订阅服务器共享和订阅该共享值。所有订户将在一个时间点执行。
- 可能是异步的
要了解更多信息,请参阅https://stackblitz.com/edit/observable-vs-promises
可观察和承诺的基本区别是:
简短回答:
可观察性更好,它有所有的承诺功能和额外的功能。
长回答:
承诺:
- 一次性使用"返回数据一次"
- 不取消
- 一个听众
- 没有套接字支持一个侦听器
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(); }); } |
如果您的组件在承诺得到解决之前被销毁,那么在承诺得到解决时,您将得到一个
或者,如果您使用takeuntil模式的Observables,那么一旦您的组件被破坏,订阅将被取消。
这是一个有点做作的例子,但是为被破坏的组件执行代码可能会导致错误。除非你真的出于某种原因想这么做:p
观察和承诺帮助我们使用javascript/typescript中的异步功能。它们在许多情况下都非常相似,但是它们之间仍然存在一些差异。
在第一次阅读教程和文档时,我遇到了一个不明显的问题,那就是多播的想法。
确保您知道,默认情况下,多个订阅将在一个可观察的表中触发多个执行。对单个HTTP调用Observable的多个订阅将触发多个相同的HTTP调用,除非您
承诺迫使您一次只处理一件事情,打开它的数据,处理异常,对诸如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); |
关于承诺:它有一个管道,因此在调用时只返回一次值。它的单向处理程序,因此一旦调用,您可能无法取消。您可以使用的有用语法有:when()和then()。
Observables:
观察数据是一段时间内多个值的惰性集合。这是一种非常好的异步操作方法。它可以与具有跨平台支持的RXJS一起完成,可以与角度/反应等一起使用。
它的行为像一条小溪。可以是多管道。所以一旦定义好,您就可以订阅在许多地方获得返回结果。
语法:
1 2 | Rx.Observable.fromEvent(button,"click"), Rx.Subject() |
等
认购:
如:
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)); |
因为它支持多个管道,所以您可以在不同的位置订阅结果,它比承诺有更多的可能性。
用途:它有更多的可能性,如
关于这个话题已经有很多答案了,所以我不会再增加一个多余的答案。
但是对于那些刚开始学习可观察/角度和怀疑哪一个与承诺相比使用的人,我建议你保持所有可观察的东西,并将你项目中的所有现有承诺转化为可观察的。
仅仅因为角框架本身和它的社区都是使用可观察的。因此,当您集成框架服务或第三方模块并将所有内容链接在一起时,这将是有益的。
虽然我很欣赏所有的落选,但我仍然坚持以上我的观点,除非有人给出适当的评论,列出一些可能在你的角度项目中仍然有用的场景,以使用承诺而不是可观察的。
当然,在所有情况下,没有一个观点是100%正确的,但至少我认为98%的时间是在角度框架中执行常规商业项目的,可观察是正确的方式。
即使你不喜欢在你的简单爱好项目的起点,你很快会意识到几乎所有的组件,你互动的角度,和大多数角度友好的第三方框架使用可观察,然后你会不断转换你的承诺,以可观察,以便与他们沟通。
这些组件包括但不限于:HTTPClient、Form Builder、Angular Material模块/对话框、NGRX Store/Effects和NGX引导。
事实上,我在过去2年里处理过的角度生态系统的唯一承诺就是