Angular Observable 和 Http
Angular Observables and Http
我很难全神贯注于 Angular 中的可观察对象。我来自 PHP 的世界,那里的事物绝对不是异步的。
我有一个组件,它只显示一般主题的消息列表。现在,我有一个所有消息都属于的主题。如果该主题不存在,则应创建它。消息和主题调用都是通过 REST api 完成的。
在非异步世界中,我会按顺序对其进行编程。消息服务将查看主题是否存在。如果没有,则由主题服务创建它。获得主题后,它会获取该主题内的所有消息。
我知道您订阅了一个可观察对象,但是当需要一系列事情按顺序发生时会发生什么? angular 2 http documentation 通过一个非常简单的例子来说明一次调用时更新英雄列表。
组件
export class NotificationListComponent implements OnInit {
constructor(private _notificationService:NotificationService) {
}
***
ngOnInit() {
this.getNotifications();
}
getNotifications() {
this._notificationService.getNotifications()
.subscribe(
notifications => this.notifications = notifications,
error => this.errorMessage = <any>error);
}
通知服务
...
getNotifications() {
// call the topic service here for general topic??
return this.http.get('/messages?order[datesent]=DESC')
.map((res) => {
return res.json()["hydra:member"];
})
.map((notifications:Array<any>) => {
let result:Array<Notification> = [];
notifications.forEach((jsonNotification) => {
var Notification:Notification = {
message: jsonNotification.message,
topic: jsonNotification.topic,
datesent: new Date(jsonNotification.datesent),
};
result.push(Notification);
});
return result;
})
.catch(this.handleError);
}
...
主题服务
...
getGeneralTopic() {
var generalTopic;
this.http.get('/topics?name=general')
.map((res) => {
return res.json()["hydra:member"][0];
})
.do(data => console.log(data))
.subscribe(generalTopic => res);
}
...
虽然我不确定是否理解您的问题,但也许 Observable.selectMany
和 Observable.and/thenDo/when
可以帮助您。
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/selectmany.md
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/when.md
在你的情况下,我认为它会让你创建一个获取和插入的事务。
如何推理 Observables?
Observables 处理 streams。流几乎可以是任何东西,但您可以将它们视为 异步事件的抽象数组 。这很有用,因为您现在可以更清楚地推理它们:
- abstract 因为 Observable 可以产生任何类型的值:
String, Boolean, Object
- array 因为 Observable 有
Operators
类似于 JavaScript 的数组方法:map(), filter(), reduce()
- of 因为 Observable 是 value(s)
的包装器
- 异步因为Observable可能或者可能不会执行
- events因为需要触发Observable
何时使用 Observables?
当您需要执行一项任务或涉及多个步骤的操作时,您通常希望使用 Observables。这可以作为您的起点,您可以稍后根据需要简化或增加复杂性。
您应该有一个“计划”,或者至少对这些步骤应该是什么有一个模糊的想法。听起来很明显,但很多 problems/issues 的发生是因为您不知道自己想要什么 (;
一旦您计划了一项行动(作为一系列步骤),您就可以从任一端开始,但我认为最好从最后开始。至少在你了解更多之前。
I have a component that simply displays a list of messages for a general topic. For now, I have one topic that all messages belong to. If the topic doesn't exist, then it should be created. The message and topic calls are all done through a REST api.
In a non-async world, I would program it in order. The message service would see if the topic exists. If it doesn't, then it has the topic service create it. After it has the topic, it then fetches all of the messages within that topic.
对于您的用例计划 将是:["(create topic)", "select topic", "show messages"]
。 messages
是抽象数组,select
和create
是异步事件.
如何使用 Observable?
正如我上面所说,让我们从头开始 - "show messages"
。
<div *ngFor="#message of messages"></div>
我们知道我们正在处理 Observable.of(messages)
(这就是您手动创建它的方式)。接下来,您需要 'fill' 流 消息,您可以使用 return 和 Observable
的 Http
服务来完成。由于您从服务器获得的消息被 Http
服务包裹在几个“层”中,我们可以利用 Observable 的能力来链接运算符(运算符 return Observable
)并获取我们的消息需要:
getMessages(topic) {
this.http.get("/messages?topic=" + topic)
.map(response => response.json())
// chain other operators here...
.filter(message => !message.private)
}
你可以在这里使用你需要的任何东西 Operators...这导致了关于 Observables 的下一件大事:
“热”和“冷”Observables
Observables 默认是 cold。这意味着当您创建一个可观察对象时,您只需描述它应该做什么。它不会立即执行这些操作(像 Promises
那样)它需要被触发。
您可以通过订阅它来触发它,可以使用 subscribe()
方法手动触发,也可以让 Angular 使其成为 hot 使用 async
管道(为您订阅)。
getMessages(topic) {
this.http.get("/messages?topic=" + topic)
.map(response => response.json())
.subscribe(messages => this.messages = messages);
}
正在观察变化
接下来要做的事情(或者之前的事情,因为我们要在 计划 中倒退)是 "select topic"
。观察所选主题的价值并通过加载新消息来响应它的变化会很好。这可以通过 Subject
.
来完成
A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.
我们可以设置 topic$
这样做:
class ListComponent {
public messages;
public topic$ = new Subject();
constructor(private http: Http) {
this.getMessages('posts');
this.topic$
.subscribe(topic => this.getMessages(topic));
}
getMessages(topic: string) {....}
selectTopic(topic: string) {
this.topic$.next(topic)
}
}
总结
最后一步是 "(create topic)"
如果不存在。假设如果主题不存在,服务器会 return 出错:
getMessages(topic: string) {
this.http.get(API_URL + topic)
.map(response => response.json())
.subscribe(
messages => this.messages = messages,
error => this.createTopic(topic)
);
}
createTopic(topic: string) {
this.http.post(API_URL + topic, { body: JSON.stringify(topic) })
.map(response => response.json())
.subscribe();
}
这里是 working plunker 这个例子。如您所见,这并不难(50 多行代码……)。您可以轻松地四处移动并在需要的地方创建服务。
事实上,响应式编程允许创建异步数据流。这意味着您可以利用
Angular2 的 HTTP 支持利用运算符(flatMap,...)将 link 流组合在一起并实现复杂的处理链。这意味着您可以使用 Reactive Programming / RxJS 的所有概念来使 HTTP 请求成为异步数据流的一部分。
这里是一个简单的示例,允许根据表单输入的值(发生更新时)执行请求:
this.inputControl.valueChanges.debounceTime(500).flatMap(
val => {
// Execute ab
return this.http.get(`http://...?filter=${val}`);
}).subscribe((data) => {
this.places = data;
});
运营商还可以允许在 HTTP 上下文中轻松实现几个问题:
确保按顺序执行请求时接收最新的(例如基于用户输入)。 switchMap
运算符示例,在触发新请求时取消之前正在进行的请求
this.ctrl.valueChanges.switchMap(
val => {
return this.http.get(`http://...?filter=${val}`);
});
缓冲事件并在一段时间后触发最后一个事件。在根据输入的最新值执行请求之前等待 500 毫秒不活动的示例:
this.ctrl.valueChanges.debounceTime(500).flatMap(
val => {
return this.http.get(`http://...?filter=${val}`);
});
请求失败时重试。每 500 毫秒和 2 秒内重试的示例
var params = new URLSearchParams();
params.set('placename_startsWith', searchText);
params.set('username', 'XXX');
return this.http.get('http://api.geonames.org/postalCodeSearchJSON',
{ search: params })
.retryWhen(error => error.delay(500))
.timeout(2000, return new Error('delay exceeded'))
.map(res => res.json().postalCodes);
这里有一些关于响应式编程的文章可以帮助你:
- André Staltz 对响应式编程的介绍 - https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
- Everything is a stream by Rob Wormald - http://slides.com/robwormald/everything-is-a-stream
我很难全神贯注于 Angular 中的可观察对象。我来自 PHP 的世界,那里的事物绝对不是异步的。
我有一个组件,它只显示一般主题的消息列表。现在,我有一个所有消息都属于的主题。如果该主题不存在,则应创建它。消息和主题调用都是通过 REST api 完成的。
在非异步世界中,我会按顺序对其进行编程。消息服务将查看主题是否存在。如果没有,则由主题服务创建它。获得主题后,它会获取该主题内的所有消息。
我知道您订阅了一个可观察对象,但是当需要一系列事情按顺序发生时会发生什么? angular 2 http documentation 通过一个非常简单的例子来说明一次调用时更新英雄列表。
组件
export class NotificationListComponent implements OnInit {
constructor(private _notificationService:NotificationService) {
}
***
ngOnInit() {
this.getNotifications();
}
getNotifications() {
this._notificationService.getNotifications()
.subscribe(
notifications => this.notifications = notifications,
error => this.errorMessage = <any>error);
}
通知服务
...
getNotifications() {
// call the topic service here for general topic??
return this.http.get('/messages?order[datesent]=DESC')
.map((res) => {
return res.json()["hydra:member"];
})
.map((notifications:Array<any>) => {
let result:Array<Notification> = [];
notifications.forEach((jsonNotification) => {
var Notification:Notification = {
message: jsonNotification.message,
topic: jsonNotification.topic,
datesent: new Date(jsonNotification.datesent),
};
result.push(Notification);
});
return result;
})
.catch(this.handleError);
}
...
主题服务
...
getGeneralTopic() {
var generalTopic;
this.http.get('/topics?name=general')
.map((res) => {
return res.json()["hydra:member"][0];
})
.do(data => console.log(data))
.subscribe(generalTopic => res);
}
...
虽然我不确定是否理解您的问题,但也许 Observable.selectMany
和 Observable.and/thenDo/when
可以帮助您。
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/selectmany.md
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/when.md
在你的情况下,我认为它会让你创建一个获取和插入的事务。
如何推理 Observables?
Observables 处理 streams。流几乎可以是任何东西,但您可以将它们视为 异步事件的抽象数组 。这很有用,因为您现在可以更清楚地推理它们:
- abstract 因为 Observable 可以产生任何类型的值:
String, Boolean, Object
- array 因为 Observable 有
Operators
类似于 JavaScript 的数组方法:map(), filter(), reduce()
- of 因为 Observable 是 value(s) 的包装器
- 异步因为Observable可能或者可能不会执行
- events因为需要触发Observable
何时使用 Observables?
当您需要执行一项任务或涉及多个步骤的操作时,您通常希望使用 Observables。这可以作为您的起点,您可以稍后根据需要简化或增加复杂性。
您应该有一个“计划”,或者至少对这些步骤应该是什么有一个模糊的想法。听起来很明显,但很多 problems/issues 的发生是因为您不知道自己想要什么 (;
一旦您计划了一项行动(作为一系列步骤),您就可以从任一端开始,但我认为最好从最后开始。至少在你了解更多之前。
I have a component that simply displays a list of messages for a general topic. For now, I have one topic that all messages belong to. If the topic doesn't exist, then it should be created. The message and topic calls are all done through a REST api.
In a non-async world, I would program it in order. The message service would see if the topic exists. If it doesn't, then it has the topic service create it. After it has the topic, it then fetches all of the messages within that topic.
对于您的用例计划 将是:["(create topic)", "select topic", "show messages"]
。 messages
是抽象数组,select
和create
是异步事件.
如何使用 Observable?
正如我上面所说,让我们从头开始 - "show messages"
。
<div *ngFor="#message of messages"></div>
我们知道我们正在处理 Observable.of(messages)
(这就是您手动创建它的方式)。接下来,您需要 'fill' 流 消息,您可以使用 return 和 Observable
的 Http
服务来完成。由于您从服务器获得的消息被 Http
服务包裹在几个“层”中,我们可以利用 Observable 的能力来链接运算符(运算符 return Observable
)并获取我们的消息需要:
getMessages(topic) {
this.http.get("/messages?topic=" + topic)
.map(response => response.json())
// chain other operators here...
.filter(message => !message.private)
}
你可以在这里使用你需要的任何东西 Operators...这导致了关于 Observables 的下一件大事:
“热”和“冷”Observables
Observables 默认是 cold。这意味着当您创建一个可观察对象时,您只需描述它应该做什么。它不会立即执行这些操作(像 Promises
那样)它需要被触发。
您可以通过订阅它来触发它,可以使用 subscribe()
方法手动触发,也可以让 Angular 使其成为 hot 使用 async
管道(为您订阅)。
getMessages(topic) {
this.http.get("/messages?topic=" + topic)
.map(response => response.json())
.subscribe(messages => this.messages = messages);
}
正在观察变化
接下来要做的事情(或者之前的事情,因为我们要在 计划 中倒退)是 "select topic"
。观察所选主题的价值并通过加载新消息来响应它的变化会很好。这可以通过 Subject
.
A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.
我们可以设置 topic$
这样做:
class ListComponent {
public messages;
public topic$ = new Subject();
constructor(private http: Http) {
this.getMessages('posts');
this.topic$
.subscribe(topic => this.getMessages(topic));
}
getMessages(topic: string) {....}
selectTopic(topic: string) {
this.topic$.next(topic)
}
}
总结
最后一步是 "(create topic)"
如果不存在。假设如果主题不存在,服务器会 return 出错:
getMessages(topic: string) {
this.http.get(API_URL + topic)
.map(response => response.json())
.subscribe(
messages => this.messages = messages,
error => this.createTopic(topic)
);
}
createTopic(topic: string) {
this.http.post(API_URL + topic, { body: JSON.stringify(topic) })
.map(response => response.json())
.subscribe();
}
这里是 working plunker 这个例子。如您所见,这并不难(50 多行代码……)。您可以轻松地四处移动并在需要的地方创建服务。
事实上,响应式编程允许创建异步数据流。这意味着您可以利用
Angular2 的 HTTP 支持利用运算符(flatMap,...)将 link 流组合在一起并实现复杂的处理链。这意味着您可以使用 Reactive Programming / RxJS 的所有概念来使 HTTP 请求成为异步数据流的一部分。
这里是一个简单的示例,允许根据表单输入的值(发生更新时)执行请求:
this.inputControl.valueChanges.debounceTime(500).flatMap(
val => {
// Execute ab
return this.http.get(`http://...?filter=${val}`);
}).subscribe((data) => {
this.places = data;
});
运营商还可以允许在 HTTP 上下文中轻松实现几个问题:
确保按顺序执行请求时接收最新的(例如基于用户输入)。
switchMap
运算符示例,在触发新请求时取消之前正在进行的请求this.ctrl.valueChanges.switchMap( val => { return this.http.get(`http://...?filter=${val}`); });
缓冲事件并在一段时间后触发最后一个事件。在根据输入的最新值执行请求之前等待 500 毫秒不活动的示例:
this.ctrl.valueChanges.debounceTime(500).flatMap( val => { return this.http.get(`http://...?filter=${val}`); });
请求失败时重试。每 500 毫秒和 2 秒内重试的示例
var params = new URLSearchParams(); params.set('placename_startsWith', searchText); params.set('username', 'XXX'); return this.http.get('http://api.geonames.org/postalCodeSearchJSON', { search: params }) .retryWhen(error => error.delay(500)) .timeout(2000, return new Error('delay exceeded')) .map(res => res.json().postalCodes);
这里有一些关于响应式编程的文章可以帮助你:
- André Staltz 对响应式编程的介绍 - https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
- Everything is a stream by Rob Wormald - http://slides.com/robwormald/everything-is-a-stream