本文翻译自:What is the difference between Promises and Observables?
Can someone please explain the difference between Promise
and Observable
in Angular? 有人可以解释Angular中Promise
和Observable
之间的区别吗?
An example on each would be helpful in understanding both the cases. 每个示例都将有助于理解这两种情况。 In what scenario can we use each case? 在什么情况下我们可以使用每种情况?
参考:https://stackoom.com/question/2WmKr/Promises和Observables有什么区别
Promise 诺言
A Promise
handles a single event when an async operation completes or fails. 当异步操作完成或失败时, Promise
处理一个事件 。
Note: There are Promise
libraries out there that support cancellation, but ES6 Promise
doesn't so far. 注意:那里有支持取消的Promise
库,但是ES6 Promise
到目前为止还不支持。
Observable 可观察的
An Observable
is like a Stream
(in many languages) and allows to pass zero or more events where the callback is called for each event. 一个Observable
就像一个Stream
(在许多语言中),并且允许传递零个或多个事件,其中每个事件都被调用回调。
Often Observable
is preferred over Promise
because it provides the features of Promise
and more. 通常, Observable
比Promise
更为可取,因为它提供了Promise
等功能。 With Observable
it doesn't matter if you want to handle 0, 1, or multiple events. 使用Observable
,您要处理0、1或多个事件都没有关系。 You can utilize the same API in each case. 在每种情况下,您都可以使用相同的API。
Observable
also has the advantage over Promise
to be cancelable . 与Promise
相比, Observable
也具有可取消的优势。 If the result of an HTTP request to a server or some other expensive async operation isn't needed anymore, the Subscription
of an Observable
allows to cancel the subscription, while a Promise
will eventually call the success or failed callback even when you don't need the notification or the result it provides anymore. 如果不再需要对服务器的HTTP请求或其他昂贵的异步操作的结果,则Observable
的Subscription
可以取消该预订,而Promise
最终将调用成功或失败的回调,即使您不需要需要通知或它提供的结果了。
Observable provides operators like map
, forEach
, reduce
, ... similar to an array Observable提供类似于数组的map
, forEach
, reduce
,...等运算符
There are also powerful operators like retry()
, or replay()
, ... that are often quite handy. 还有一些功能强大的运算符,例如retry()
或replay()
……通常非常方便。
Both Promises
and Observables
provide us with abstractions that help us deal with the asynchronous nature of our applications. Promises
和Observables
为我们提供了抽象,可以帮助我们处理应用程序的异步性质。 The difference between them was pointed out clearly by @Günter and @Relu. @Günter和@Relu明确指出了它们之间的区别。
Since a code snippet is worth a thousand words, let go through the below example to understand them easier. 由于一个代码片段包含一千个单词,因此下面的示例使它们更容易理解。
Thanks @Christoph Burgdorf for the awesome article 感谢@Christoph Burgdorf的精彩文章
Angular uses Rx.js Observables instead of promises for dealing with HTTP. Angular使用Rx.js Observables代替了用于处理HTTP的Promise。
Suppose that you are building a search function that should instantly show you results as you type. 假设您正在构建一个搜索功能 ,该功能应在您键入时立即显示结果。 Sound familiar but there are a lot of challenges that come with that task. 听起来很熟悉,但是该任务面临很多挑战。
HTTP
requests. 我们不想每次用户按下一个键时都击中服务器端点,它应该向他们发出大量HTTP
请求。 Basically, we only want to hit it once the user has stopped typing instead of with every keystroke. 基本上,我们只希望在用户停止键入后才敲击它,而不是每次击键都击中它。 The demo will simply consist of two files: app.ts
and wikipedia-service.ts
. 该演示将仅包含两个文件: app.ts
和wikipedia-service.ts
。 In a real world scenario, we would most likely split things further up, though. 不过,在现实世界中,我们很可能会将事情进一步拆分。
Below is Promise-based implementation that doesn't handle any of the described edge cases. 以下是基于Promise的实现,该实现不处理任何描述的极端情况。
wikipedia-service.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]);
}
}
We are injecting the Jsonp
service to make a GET
request against the Wikipedia API with a given search term. 我们正在注入Jsonp
服务,以使用给定的搜索词针对Wikipedia API发出GET
请求。 Notice that we call toPromise
in order to get from an Observable
to a Promise
. 注意,我们调用toPromise
以便从Observable
到达Promise
。 Eventually end up with a Promise
as the return type of our search method. 最终以Promise
作为搜索方法的返回类型。
app.ts
// check the plnkr for the full list of imports
import {...} from '...';
@Component({
selector: 'my-app',
template: `
Wikipedia Search
- {{item}}
`
})
export class AppComponent {
items: Array;
constructor(private wikipediaService: WikipediaService) {}
search(term) {
this.wikipediaService.search(term)
.then(items => this.items = items);
}
}
Not much of a surprise here either. 这里也没有什么惊喜。 We inject our WikipediaService
and expose it's functionality via a search method to the template. 我们注入WikipediaService
并通过搜索方法将其功能公开给模板。 The template simply binds to keyup and calls search(term.value)
. 该模板仅绑定到keyup并调用search(term.value)
。
We unwrap the result of the Promise that the search method of the WikipediaService returns and expose it as a simple Array of strings to the template so that we can have *ngFor
loop through it and build up a list for us. 我们解开Promise的结果,即WikipediaService的搜索方法返回并将其作为简单的字符串数组公开给模板,以便我们可以通过*ngFor
循环并为我们建立一个列表。
See the example of Promise-based implementation on Plunker 请参阅在Plunker上基于Promise的实现示例
Where Observables really shine 可观察的事物真正发光的地方
Let's change our code to not hammer the endpoint with every keystroke but instead only send a request when the user stopped typing for 400 ms 让我们更改代码,以免每次敲击都敲击端点,而仅在用户停止键入400毫秒时发送请求
To unveil such super powers we first need to get an Observable
that carries the search term that the user types in. Instead of manually binding to the keyup event, we can take advantage of Angular's formControl
directive. 为了揭示这种超能力,我们首先需要获得一个Observable
,其中带有用户键入的搜索词。我们可以利用Angular的formControl
指令来代替手动绑定到keyup事件。 To use this directive, we first need to import the ReactiveFormsModule
into our application module. 要使用此指令,我们首先需要将ReactiveFormsModule
导入到我们的应用程序模块中。
app.ts
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 {}
Once imported, we can use formControl from within our template and set it to the name "term". 导入后,我们可以在模板中使用formControl并将其设置为名称“ term”。
In our component, we create an instance of FormControl
from @angular/form
and expose it as a field under the name term on our component. 在我们的组件中,我们从@angular/form
创建FormControl
的实例,并将其作为字段公开为组件下名称项。
Behind the scenes, term automatically exposes an Observable
as property valueChanges
that we can subscribe to. 在幕后, term自动将Observable
公开为我们可以订阅的属性valueChanges
。 Now that we have an Observable
, overcoming the user input is as easy as calling debounceTime(400)
on our Observable
. 现在我们有了一个Observable
,克服用户输入就像在Observable
上调用debounceTime(400)
一样容易。 This will return a new Observable
that will only emit a new value when there haven't been coming new values for 400ms. 这将返回一个新的Observable
,仅当没有持续400ms的新值时才会发出新值。
export class App {
items: Array;
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));
}
}
It would be a waste of resources to send out another request for a search term that our app already shows the results for. 发送另一个对我们的应用程序已经显示出搜索结果的搜索词的请求,将浪费资源。 All we have to do to achieve the desired behavior is to call the distinctUntilChanged
operator right after we called debounceTime(400)
所有我们需要做的,以实现所需的行为是调用distinctUntilChanged
操作正确的,我们叫做后debounceTime(400)
See the example of Observable implementation on Plunker 请参阅Plunker上的Observable实现示例
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 要处理乱序响应,请查看全文http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
As far as I am using Http in Angular, I agree that in the normal use cases there is not much difference when using Observable over Promise. 就我在Angular中使用Http而言,我同意在正常使用情况下使用Observable over Promise并没有太大区别。 None of the advantages are really relevant here in practice. 在实践中,这些优势都没有真正相关。 Hope I can see some advanced use case in the future :) 希望以后能看到一些高级用例:)
Learn more 学到更多
- https://angular-2-training-book.rangle.io/handout/observables/ https://angular-2-training-book.rangle.io/handout/observables/
- https://angular.io/tutorial/toh-pt6#observables https://angular.io/tutorial/toh-pt6#observables
Promises 承诺
Observables 可观察的
One operator retry can be used to retry whenever needed, also if we need to retry the observable based on some conditions retryWhen can be used. 可以在需要时使用一个运算符重试来重试,如果需要根据某些条件重试可观察的对象,也可以使用retryWhen 。
Note : A list of operators along with their interactive diagrams is available here at RxMarbles.com 注意 :可以在RxMarbles.com上找到操作员列表及其交互图。
I've just dealt with an issue where Promises were the best solution, and I'm sharing it here for anyone stumbling across this question in the event it's useful (this was exactly the answer I was looking for earlier): 我刚刚处理了一个问题,其中“承诺”是最好的解决方案,我在这里与任何共享它的人分享,以免遇到任何有用的问题(这正是我之前所寻找的答案):
In an Angular2 project I have a service that takes some parameters and returns a value list to populate drop down menus on a form. 在Angular2项目中,我有一个接受一些参数并返回值列表以填充表单上下拉菜单的服务。 When the form component initializes, I need to call the same service multiple times with different parameters to define a number of different dropdown menus, however if I simply queue up all the variables to call the service, only the last one succeeds and the rest error out. 表单组件初始化时,我需要使用不同的参数多次调用同一服务以定义多个不同的下拉菜单,但是,如果我只是将所有变量排队,以调用该服务,则只有最后一个成功,其余错误出来。 The service fetching from the database could only handle one request at a time. 从数据库中获取的服务一次只能处理一个请求。
The only way to successfully populate all the dropdown menu variables was to call the service in a way that prevented a new request from being processed until the last request was finished, and the Promise / .then mechanism solved the problem nicely. 成功填充所有下拉菜单变量的唯一方法是调用服务,以防止在最后一个请求完成之前处理新请求,并且Promise / .then机制很好地解决了该问题。
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;
}) }
I defined the functions in the component, and then called initializeDropDowns() in ngOnInit. 我在组件中定义了函数,然后在ngOnInit中调用了initializeDropDowns()。
The fetchValueList function returns a Promise, so the first call passes the first listCode and when the Promise resolves, the return value is in the data variable in the .then block where we can assign it to the this.firstValList variable. fetchValueList函数返回一个Promise,因此第一个调用传递第一个listCode,当Promise解析时,返回值位于.then块中的data变量中,可以在其中将其分配给this.firstValList变量。 As the function has returned data, we know the service has finished and it's safe to call again with the second listCode, the return value is in the data variable in the next .then block and we assign it to the this.secondValList variable. 由于函数已返回数据,因此我们知道服务已完成,可以安全地使用第二个listCode再次调用,返回值位于下一个.then块的data变量中,并将其分配给this.secondValList变量。
We can chain this as many times as required to populate all the variables, and on the last code block we simply omit the return statement and the block terminates. 我们可以根据需要将其链接多次,以填充所有变量,并且在最后一个代码块中,我们只需省略return语句,然后该块终止。
This is a very specific use case where we have a single service that needs to be called multiple times as the component initializes, and where the service has to complete its fetch and return a value before it can be called again, but in this case, the Promise / .then method was ideal. 这是一个非常特殊的用例,其中我们需要在组件初始化时多次调用一个服务,并且该服务必须完成获取并返回值才能再次调用它,但是在这种情况下, Promise / .then方法是理想的。
Both Promises and Observables will help us work with the asynchronous functionalities in JavaScript. Promises和Observables都将帮助我们使用JavaScript中的异步功能 。 They are very similar in many cases, however, there are still some differences between the two as well, promises are values that will resolve in asynchronous
ways like http calls. 它们在许多情况下非常相似,但是两者之间仍然存在一些差异,promise是将以asynchronous
方式(如http调用)解析的值。 On the other hand, observables deal with a sequence of asynchronous events . 另一方面,可观察对象处理一系列异步事件 。 The main differences between them are listed below: 它们之间的主要区别如下:
promise: 诺言:
observable: 可观察的:
Also, I've created the graphical image for you below to show the differences visually: 另外,我还在下面为您创建了图形图像,以直观地显示差异: