Promise
和Observable
Angular有什么区别?
每个示例都将有助于理解这两种情况。在什么情况下我们可以使用每种情况?
Promise
和Observable
Angular有什么区别?
每个示例都将有助于理解这两种情况。在什么情况下我们可以使用每种情况?
Answers:
诺言
当异步操作完成或失败时,A Promise
处理单个事件。
注意:那里Promise
有支持取消的库,但是ES6 Promise
到目前为止还不支持。
可观察的
An Observable
就像Stream
(在许多语言中)一样,并且允许传递零个或多个事件,其中每个事件都将调用回调。
通常Observable
,Promise
它是首选,因为它具有Promise
和的功能。有了Observable
它,如果你想处理0,1或多个事件没有关系。在每种情况下,您都可以使用相同的API。
Observable
相Promise
对于取消也有优势。如果不再需要对服务器的HTTP请求或其他昂贵的异步操作的结果,则Subscription
的Observable
可以取消订阅,而Promise
最终会调用成功或失败的回调,即使您不需要通知也是如此或其提供的结果。
可观察到的提供运营商等map
,forEach
,reduce
,...相似的阵列
还有一些功能强大的运算符,例如retry()
或replay()
,...常常非常方便。
Promise
与async
/一起await
使代码再次变得平坦!在大多数情况下,以及在不涉及火箭科学的项目中,都不需要使用不必要的复杂方法链来编写那些可怕的嵌套函数。今天,您可以将async
/ await
与转译器一起使用,例如TypeScript
,并且无需任何rxjs
样板即可编写实际的人类可读平面代码。rxjs
在某些情况下,您有时可能仍需要,因为它确实提供了很多东西。
双方Promises
并Observables
提供给我们,帮助我们应对抽象异步我们的应用程序的性质。@Günter和@Relu明确指出了它们之间的区别。
由于一个代码片段包含一千个单词,因此下面的示例使它们更容易理解。
感谢@Christoph Burgdorf的精彩文章
Angular使用Rx.js Observables代替了用于处理HTTP的Promise。
假设您正在构建一个搜索功能,该功能应在您键入时立即显示结果。听起来很熟悉,但是该任务面临很多挑战。
HTTP
请求。基本上,我们只希望在用户停止键入后才敲击它,而不是每次击键都击中它。该演示将仅包含两个文件:app.ts
和wikipedia-service.ts
。但是,在现实世界中,我们很可能会将事情进一步拆分。
以下是基于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]);
}
}
我们正在注入该Jsonp
服务,以使用给定的搜索词GET
针对Wikipedia API发出请求。请注意,我们toPromise
为了从Observable<Response>
到进行调用Promise<Response>
。最终以a 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);
}
}
这里也没有什么惊喜。我们WikipediaService
通过模板的搜索方法注入并公开其功能。该模板仅绑定到keyup和call search(term.value)
。
我们解开Promise的结果,即WikipediaService的搜索方法返回并将其作为简单的字符串数组公开给模板,以便我们可以*ngFor
遍历它并为我们建立一个列表。
当观测量大放异彩
让我们更改代码,以免每次敲击都敲击端点,而仅在用户停止键入400毫秒时发送请求
为了揭示这种超级能力,我们首先需要获取一个Observable<string>
带有用户键入搜索词的工具。我们可以利用Angular的formControl
指令,而不是手动绑定到keyup事件。要使用此指令,我们首先需要将导入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 {}
导入后,我们可以在模板中使用formControl并将其设置为名称“ term”。
<input type="text" [formControl]="term"/>
在我们的组件中,我们创建一个FormControl
from 的实例,@angular/form
并将其公开为一个字段,该字段位于我们组件上的名称术语下。
在幕后,term自动显示我们可以订阅的Observable<string>
as属性valueChanges
。现在我们有了一个Observable<string>
,克服用户输入就像调用debounceTime(400)
我们的一样容易Observable
。这将返回一个新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));
}
}
发送另一个对我们的应用程序已经显示出搜索结果的搜索词的请求,将浪费资源。为了实现所需的行为,我们要做的就是在调用distinctUntilChanged
后立即调用操作符debounceTime(400)
对于处理乱序的响应,请查看全文 http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
就我在Angular中使用Http而言,我同意在正常使用情况下使用Observable over Promise并没有太大区别。在实践中,这些优势都没有真正相关。希望以后能看到一些高级用例:)
学到更多
双方承诺和观测量将有助于我们的工作与异步功能在JavaScript中。它们在许多情况下非常相似,但是两者之间仍然存在一些差异,promise是可以asynchronous
通过http调用等方式解析的值。另一方面,可观察对象处理一系列异步事件。它们之间的主要区别如下:
诺言:
可观察的:
另外,我还在下面为您创建了图形图像,以直观地显示差异:
Promise
是思考承诺方式的错误方式。该Promise
公司的IT负责只处理成功或失败的异步兼容的方式。如果你想取消您取消请求,而不是承诺的HTTP请求,并作出取消或者履行或拒绝承诺的结果。jsfiddle.net/greggman/ea0yhd4p
承诺
可观察的
可以在需要时使用一个运算符重试来重试,如果需要根据某些条件重试可观察的对象,也可以使用retryWhen。
注意:可以在RxMarbles.com上获得操作员列表及其交互图。
答案中缺少Observables的缺点。承诺允许使用ES7异步/等待功能。使用它们,您可以编写异步代码,就像执行同步函数调用一样,因此您不再需要回调。Observables这样做的唯一可能性是将它们转换为Promises。但是,当您将它们转换为Promises时,您只能再次获得一个返回值:
async function getData(){
const data = await observable.first().toPromise();
//do stuff with 'data' (no callback function needed)
}
进一步阅读:如何在Rx Observable上等待?
Promises和Observables都仅处理异步调用。
这是它们之间的区别:
可观察的
诺言
一次仅发出一个值
调用没有.then和.catch的服务
无法取消
不提供任何运算符
即使这个答案很晚,我还是总结了以下差异,
可观察的:
function
需要an observer
并返回一个 function Observer: an object with next, error.
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()
返回可以调用方法的Observable-- Observer Observable.from()
将数组或Iterable转换为Observable Observable.fromEvent()
--将事件转换为Observable-- Observable.fromPromise()
将Promise转换为Observable-- Observable.range()
返回指定范围内的整数序列承诺:
许诺代表了一项将在未来完成的任务;
诺言成为resolved by a value
;
承诺被例外拒绝;
不cancellable
,它返回a single value
一个承诺公开功能 (then)
-然后返回一个新的 promise
;
的-允许attachment
将基于
state
;
- handlers
是 guaranteed
执行中order attached
;
我刚刚处理了一个问题,其中“承诺”是最好的解决方案,我在这里与任何共享它的人分享,以免遇到任何有用的问题(这正是我之前所寻找的答案):
在Angular2项目中,我有一个接受一些参数并返回值列表以填充表单上下拉菜单的服务。表单组件初始化时,我需要使用不同的参数多次调用同一服务以定义多个不同的下拉菜单,但是,如果我只是将所有变量排队,以调用该服务,则只有最后一个成功,其余错误出来。从数据库中获取的服务一次只能处理一个请求。
成功填充所有下拉菜单变量的唯一方法是调用服务,以防止在最后一个请求完成之前处理新请求,并且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;
}) }
我在组件中定义了函数,然后在ngOnInit中调用了initializeDropDowns()。
fetchValueList函数返回一个Promise,因此第一个调用传递第一个listCode,当Promise解析时,返回值位于.then块中的data变量中,可以在其中将其分配给this.firstValList变量。由于函数已返回数据,因此我们知道服务已完成,可以安全地使用第二个listCode再次调用,返回值位于下一个.then块的data变量中,并将其分配给this.secondValList变量。
我们可以根据需要将其链接多次,以填充所有变量,并且在最后一个代码块中,我们只需省略return语句,然后该块终止。
这是一个非常特殊的用例,其中我们有一个服务需要在组件初始化时多次调用,并且该服务必须完成获取并返回值才能再次调用,但在这种情况下, Promise / .then方法是理想的。
scan()
构建顺序可观察的流。但是,您的方法可能更明确,更容易理解。
我相信所有其他答案都应该清除您的疑问。尽管如此,我只是想补充一点,可观察对象是基于函数式编程的,并且我发现它所附带的函数非常有用,例如map,flatmap,reduce,zip。Web实现的一致性,特别是当它依赖于API请求时,是一种残酷的改进。
我强烈推荐该文档,因为它是reactX的正式文档,并且我认为它是最清晰的文档。
如果您想进入可观察的领域,我建议这篇文章分为三部分:http : //blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
尽管它是用于RxJava的,但概念是相同的,并且确实有很好的解释。在reactX文档中,您具有每个功能的等效项。您必须寻找RxJS。
您可以始终使用可观察对象来处理异步行为,因为可观察对象具有诺言提供的所有功能(+额外)。但是,有时不需要Observables提供的此额外功能。然后,导入一个库以供使用它们将是额外的开销。
当您有单个异步操作要处理结果时,请使用promise 。例如:
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()
调用该方法。
当您需要处理随时间流逝的(数据)流时,请使用Observables 。流是随时间推移变得可用的一系列数据元素。流的示例包括:
当下一个事件发生,何时发生错误或Observable 完成时,在Observable自身中指定。然后,我们可以订阅此可观察对象,从而激活它,并在此订阅中,我们可以传递3个回调(不一定总是传递所有回调)。要成功执行一次回调,就错误执行一次回调,并完成一次回调。例如:
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
创建可观察对象时,它需要一个回调函数,该函数提供观察者作为参数。在此观察,你可以接着调用onNext
,onCompleted
,onError
。然后,当Observable订阅时,它将调用传递到订阅中的相应回调。
承诺-提供单一的未来价值。不偷懒。不可取消。它将拒绝或解决。
可观察-提供多个未来价值。懒。可以取消。它提供实时地图,过滤器,减少其他方法。
const promise = new Promise(resolve => {
setTimeout(() => {
resolve("Hello from a Promise!");
}, 2000);
});
promise.then(value => console.log(value));
现在可观察的例子。在这里,我们还将一个函数传递给observable,一个观察者来处理异步任务。与承诺中的解决方法不同,它具有以下方法,然后代替订阅。
因此,两者都处理异步任务。现在让我们看一下区别。
const observable = new Observable(observer => {
setTimeout(() => {
observer.next('Hello from a Observable!');
}, 2000);
});
observable.subscribe(value => console.log(value));
诺言
可观察的
Promises和Observables都可以帮助我们处理异步操作。这些异步操作完成后,他们可以调用某些回调。
Angular使用来自RxJS的Observables代替了处理HTTP的承诺
Below are some important differences in promises & Observables.
当异步活动完成或失败时,Promise会发出一个事件。
一个Observable就像一个Stream(在许多语言中),并且允许传递至少零个或多个事件,其中每个事件都需要回调。
与Promise相比,“ Foquently Observable”更受青睐,因为它提供了Promise的亮点以及更多内容。使用Observable,您是否需要处理0、1或各种事件都没有关系。您可以针对每种情况使用类似的API。
承诺: 承诺散发出单一价值
例如:
const numberPromise = new Promise((resolve) => {
resolve(5);
resolve(10);
});
numberPromise.then(value => console.log(value));
// still prints only 5
可观察: 在一段时间内发出多个值
例如:
const numberObservable = new Observable((observer) => {
observer.next(5);
observer.next(10);
});
numberObservable.subscribe(value => console.log(value));
// prints 5 and 10
我们可以想到一个可观察对象,就像一个流,该流在一段时间内发出多个值,并且为每个发出的项调用相同的回调函数,因此对于可观察对象,我们可以使用相同的API来处理异步数据。该数据是在一段时间内作为单个值还是多个值传输的。
诺言:
可观察的:
Promise发出一个值,而Observable发出多个值。因此,在处理HTTP请求时,Promise可以管理同一请求的单个响应,但是如果对同一请求有多个响应,那么我们必须使用Observable。是的,Observable可以处理同一请求的多个响应。
诺言
const promise = new Promise((data) =>
{ data(1);
data(2);
data(3); })
.then(element => console.log(‘Promise ‘ + element));
输出量
Promise 1
可观察的
const observable = new Observable((data) => {
data.next(1);
data.next(2);
data.next(3);
}).subscribe(element => console.log('Observable ' + element));
输出量
Observable 1
Observable 2
Observable 3
以下是Promise和Observables的一些重要区别。
诺言
可观察的
为了更好地理解,请参阅https://stackblitz.com/edit/observable-vs-promises
我看到很多人使用Observable是“ cancellable”的说法,但是将Promise设置为“ cancellable”却很简单。
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
简短答案:
可观察性更好,它具有所有Promises功能以及其他功能。
长答案:
承诺:
可观察的:
虽然普遍接受的答案是好的,但我认为它并不强调在处理Angular Components时,您几乎总是要使用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();
});
}
如果在解决诺言之前销毁了组件,那么attempt to use destroyed view
在解决诺言时会出现错误。
或者,如果您使用带takeUntil模式的可观察对象,那么一旦组件被破坏,订阅将被取消。
这是一个人为的示例,但是为被破坏的组件执行代码可能会导致错误。除非您出于某种原因实际上想要这样做:p
初读本教程和文档时,我碰到的事情并没有多播的想法。
确保您知道默认情况下,多个订阅将在Observable中触发多个执行。单个HTTP调用的多个预订Observable会触发多个相同的HTTP调用,除非您.share()
(启用多播)。
一个诺言会迫使您一次处理一件事情,拆开其数据,处理异常,对异步/等待之类的很酷的事物提供语言支持,否则就是准系统。
一个Observable有很多风吹草动,但是您需要了解使用的功能,否则它可能会被滥用。
诺言:
异步事件处理程序-Promise对象表示异步操作的最终完成(或失败)及其结果值。
语法: new Promise(执行程序);
例如:
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);
关于Promise: 它只有一个管道,因此调用时仅返回一次值。它的单向处理程序,因此一旦调用,您可能无法取消。您可以在when()和then()周围使用的有用语法
可观察的:
可观察值是随时间变化的多个值的惰性集合。对于异步操作而言,这确实是一个很好的方法。可以使用具有跨平台支持的rxjs来完成,可以与angular / react等一起使用。
它的行为就像流线一样。可以是多管道。因此,一旦定义,您就可以在许多地方订阅以获得返回结果。
语法: import * as Rx from "@reactivex/rxjs";
初始化:
Rx.Observable.fromEvent(button, "click"),
Rx.Subject()
等等
认购: RxLogger.getInstance();
例如:
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));
由于它支持多管道,因此您可以在不同位置订阅结果, 因此它比承诺的可能性要大得多。
用法:
它有更多的可能性,例如map, filter, pipe, map, concatMap etc
可观察的事物经常被与诺言相提并论。以下是一些主要区别:
观测值是声明性的;直到订阅,计算才开始。承诺在创建后立即执行。这使可观测对象对于定义可以在需要结果时运行的配方非常有用。
观察值提供许多值。承诺提供一个。这使得可观察变量对于随着时间的推移获取多个值很有用。
观察对象区分链接和订阅。承诺只有.then()子句。这使得可观察对象对于创建要由系统其他部分使用的复杂转换配方有用,而不会导致工作被执行。
Observables subscription()负责处理错误。诺言将错误推向孩子的诺言。这使得可观察对象对于集中式和可预测的错误处理很有用。
这是您在ANGULAR.IO文档中可能发现的最简单的区别。其余的答案在大多数地方都是正确的
关于这个话题已经有很多答案,所以我不会添加多余的答案。
但是对于刚开始学习Observable / Angular并想与Promise进行比较的人来说,我建议您保持所有Observable并将项目中所有现有Promises转换为Observable。
仅仅是因为Angular框架本身及其社区都在使用Observable。因此,当您集成框架服务或第三方模块并将所有内容链接在一起时,这将是有益的。
虽然我很感谢所有的反对意见,但是我仍然坚持我的观点,除非有人发表适当的评论以列出一些可能仍在您的Angular项目中使用Promise over Observables的方案。
当然,在所有情况下都没有100%正确的意见,但至少我认为在Angular框架中实施常规商业项目的98%的时间中,Observable是正确的方法。
即使您在简单的业余项目的开始就不喜欢它,您也会很快意识到与Angular进行交互的几乎所有组件,并且大多数Angular友好的3rd party框架都在使用Observables,然后您将最终不断地将您的Promise转换为Observable以便与他们交流。
这些组件包括但不限于:HttpClient,表单构建器,Angular材质模块/对话框,Ngrx存储/效果和ngx-bootstrap。
实际上,我在过去两年中处理的唯一关于Angular生态系统的承诺是APP_INITIALIZER
。