Promises和Observables有什么区别?

本文翻译自:What is the difference between Promises and Observables?

Can someone please explain the difference between Promise and Observable in Angular? 有人可以解释Angular中PromiseObservable之间的区别吗?

An example on each would be helpful in understanding both the cases. 每个示例都将有助于理解这两种情况。 In what scenario can we use each case? 在什么情况下我们可以使用每种情况?


#1楼

参考:https://stackoom.com/question/2WmKr/Promises和Observables有什么区别


#2楼

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. 通常, ObservablePromise更为可取,因为它提供了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请求或其他昂贵的异步操作的结果,则ObservableSubscription可以取消该预订,而Promise最终将调用成功或失败的回调,即使您不需要需要通知或它提供的结果了。

Observable provides operators like map , forEach , reduce , ... similar to an array Observable提供类似于数组的mapforEachreduce ,...等运算符

There are also powerful operators like retry() , or replay() , ... that are often quite handy. 还有一些功能强大的运算符,例如retry()replay() ……通常非常方便。


#3楼

Both Promises and Observables provide us with abstractions that help us deal with the asynchronous nature of our applications. PromisesObservables为我们提供了抽象,可以帮助我们处理应用程序的异步性质。 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. 听起来很熟悉,但是该任务面临很多挑战。

  • We don't want to hit the server endpoint every time user presses a key, it should flood them with a storm of HTTP requests. 我们不想每次用户按下一个键时都击中服务器端点,它应该向他们发出大量HTTP请求。 Basically, we only want to hit it once the user has stopped typing instead of with every keystroke. 基本上,我们只希望在用户停止键入后才敲击它,而不是每次击键都击中它。
  • Don't hit the search endpoint with the same query params for subsequent requests. 不要为后续请求使用具有相同查询参数的搜索端点。
  • Deal with out-of-order responses. 处理乱序响应。 When we have multiple requests in-flight at the same time we must account for cases where they come back in unexpected order. 当我们同时有多个请求进行时,我们必须考虑到它们以意外顺序返回的情况。 Imagine we first type computer , stop, a request goes out, we type car , stop, a request goes out. 想象一下,我们先键入计算机 ,停止,一个请求熄灭,我们键入汽车 ,停止,一个请求熄灭。 Now we have two requests in-flight. 现在,我们有两个正在进行的请求。 Unfortunately, the request that carries the results for computer comes back after the request that carries the results for car . 不幸的是,载有计算机结果的请求在载有汽车结果的请求之后又回来了。

The demo will simply consist of two files: app.ts and wikipedia-service.ts . 该演示将仅包含两个文件: app.tswikipedia-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<Response> to a Promise<Response> . 注意,我们调用toPromise以便从Observable<Response>到达Promise<Response> Eventually end up with a Promise<Array<string>> as the return type of our search method. 最终以Promise<Array<string>>作为搜索方法的返回类型。

app.ts

// 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);
  }
}

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<string> 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<string> ,其中带有用户键入的搜索词。我们可以利用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”。

<input type="text" [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<string> as property valueChanges that we can subscribe to. 在幕后, term自动将Observable<string>公开为我们可以订阅的属性valueChanges Now that we have an Observable<string> , overcoming the user input is as easy as calling debounceTime(400) on our Observable . 现在我们有了一个Observable<string> ,克服用户输入就像在Observable上调用debounceTime(400)一样容易。 This will return a new Observable<string> that will only emit a new value when there haven't been coming new values for 400ms. 这将返回一个新的Observable<string> ,仅当没有持续400ms的新值时才会发出新值。

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));
  }
}

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 学到更多


#4楼

Promises 承诺

  1. Definition: Helps you run functions asynchronously, and use their return values (or exceptions) but only once when executed. 定义:帮助您异步运行函数,并使用它们的返回值(或异常),但在执行时仅使用一次
  2. Not Lazy 不偷懒
  3. Not cancellable( There are Promise libraries out there that support cancellation, but ES6 Promise doesn't so far). 不可取消(那里有Promise库支持取消,但是ES6 Promise到目前为止还不支持)。 The two possible decisions are 两个可能的决定是
    • Reject 拒绝
    • Resolve 解决
  4. Cannot be retried (Promises should have access to the original function that returned the promise to have a retry capability, which is a bad practice) 无法重试 (承诺应有权访问返回了具有重试功能的诺言的原始函数,这是一种不好的做法)

Observables 可观察的

  1. Definition: Helps you run functions asynchronously, and use their return values in a continuous sequence( multiple times ) when executed. 定义:帮助您异步运行函数,并在执行时以连续顺序( 多次 )使用它们的返回值。
  2. By default, it is Lazy as it emits values when time progresses. 默认情况下,它是惰性的,因为随着时间的推移它会发出值。
  3. Has a lot of operators which simplifies the coding effort. 有很多运算符,简化了编码工作。
  4. 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上找到操作员列表及其交互图。


#5楼

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<any> {
      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方法是理想的。


#6楼

Both Promises and Observables will help us work with the asynchronous functionalities in JavaScript. PromisesObservables都将帮助我们使用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: 诺言:

  • having one pipeline 有一条管道
  • usually only use with async data return 通常只用于异步数据返回
  • not easy to cancel 不容易取消

observable: 可观察的:

  • are cancellable 可以取消
  • are re-triable by nature such as retry and retryWhen 本质上可以重试,例如重试和重试时间
  • stream data in multiple pipelines 在多个管道中传输数据
  • having array-like operations like map, filter etc 具有类似数组的操作,例如地图,过滤器等
  • can be created from other sources like events 可以从事件等其他来源创建
  • they are functions, which could be subscribed later on 它们是函数,以后可以订阅

Also, I've created the graphical image for you below to show the differences visually: 另外,我还在下面为您创建了图形图像,以直观地显示差异:

承诺和可观察的图像

发布了0 篇原创文章 · 获赞 73 · 访问量 55万+

猜你喜欢

转载自blog.csdn.net/w36680130/article/details/105243904
今日推荐