处理AJAX请求时使用可观察对象的原因



我已经开始使用响应式扩展(RxJS),我明白当数据在时间上分散(或数据流)时,使用Observables是多么强大。不观察鼠标按下事件等。但是我正在努力理解它们在API调用上下文中的用例

当我进行API调用时,我有一个服务器返回我5个名称。它是一个常规的GET请求,在一个响应中返回所有5个名称。为什么我要用可观察对象而不是Promise ?我不太理解在API调用中使用可观察对象。

请帮忙,谢谢。

可观察对象是promise的超集。

如果你可以用Promise做,那么你也可以用Observable做。你可能坚持Promises的一个原因是,Observable没有内置的语法糖在JavaScript (async await)。

把两者混在一起是不可取的。没有性能或互操作性问题,但如果你打算使用RxJS observable,那么最清楚的(可维护性、可扩展性、可调试性)就是尽可能地坚持使用observable。

为什么要使用可观察对象?

程序经常在一段时间内执行任务。你可以通过将数据作为一个流来处理,从而抽象出一个回调级别。

不是

async function userButtonPress(event){
const response = await apiCall(event.thingy);
/* More code */
}

userButtonPresses$.pipe(
mergeMap(event => apiCall(event.thingy))
).subscribe(response => {
/* More code */
});

这样更好吗?嗯,是的,它意味着你正在使用按钮按下作为数据流,而不是作为回调函数。好处是多方面的。

每次点击按钮都会启动另一个并发api调用

如果事件回调中的api调用没有被管理,它就会像这样。

userButtonPresses$.pipe(
mergeMap(event => apiCall(event.thingy))
).subscribe(response => {
/* More code */
});

每次点击按钮都排队等待另一个api调用,直到前一个调用完成才开始

如果你想让Promise等待轮到你,你需要一个库或数据结构,让你的函数知道什么时候可以开始。

userButtonPresses$.pipe(
concatMap(event => apiCall(event.thingy))
).subscribe(response => {
/* More code */
});

每个按钮点击取消正在进行的api调用(如果有)并开始一个新的

Promises没有原生的方式来取消正在运行的进程,所以你需要一个库来扩展承诺和一些额外的功能。这是在你需要管理并发承诺之上的。

userButtonPresses$.pipe(
switchMap(event => apiCall(event.thingy))
).subscribe(response => {
/* More code */
});

到目前为止按了多少次按钮?

userButtonPresses$.pipe(
switchMap((event, index) => apiCall(event.thingy).pipe(
map(response => ({response, index}))
))
).subscribe(({response, index}) => {
console.log(`This is response #${index}: `, response);
/* More code */
});

忽略按键间隔时间小于1秒的

有了承诺,你可以用const time = Date.now()设置一个变量(范围在你的回调函数之外),看看在开始你的api调用之前是否已经经过了1秒。在RxJS中,这是一个简单的操作符。

我们仍然会计算我们忽略的按钮按下的次数,但是我们会忽略太靠近的按钮按下的次数。

userButtonPresses$.pipe(
map((event, index) => ({event, index})),
throttleTime(1000),
switchMap(({event, index}) => apiCall(event.thingy).pipe(
map(response => ({response, index}))
))
).subscribe(({response, index}) => {
console.log(`This is response #${index}: `, response);
/* More code */
});

按钮按下开始轮询API端点每500ms

我们仍然会计算按钮按下的次数,并限制它们(为什么不呢?)

userButtonPresses$.pipe(
map((event, index) => ({event, index})),
throttleTime(1000),
switchMap(({event, index}) => timer(0, 500).pipe(
concatMap(_ => apiCall(event.thingy).pipe(
map(response => ({response, index}))
))
)
).subscribe(({response, index}) => {
console.log(`This is response #${index}: `, response);
/* More code */
});

承诺示例

在这里,您将需要变量来手动处理所有内容。它更难测试,也不能保证其他进程不会干扰这些变量。即使是一些简单的事情,比如取消之前的api调用,如果下一个按钮在调用完成之前点击,也不能与本机承诺一起工作。

下面是计算按钮按下次数的方法

let buttonPresses = 0;
async function userButtonPress(event){
// There's no saying how often the global buttonPresses will be incremended while 
// we await this promise, so we need a local copy that doesn't change.
const inScopeButtonPresses = buttonPresses++;
const response = await apiCall(event.thingy);
console.log(`This is response #${inScopeButtonPresses}: `, response);
/* More code */
}

Angular中的可观察对象

这就是Angular Team关于observable:

observable提供了在应用程序各部分之间传递消息的支持。它们经常在Angular中使用和是事件处理、异步编程和处理多个值的推荐技术。

一个可观察对象可以传递任何类型的多个值——字面量、消息或事件,这取决于上下文。用于接收值的API是相同的,无论这些值是同步传递还是异步传递。因为设置和拆除逻辑都是由可观察对象处理的,所以你的应用程序代码只需要担心订阅消费值,以及在订阅完成后取消订阅。流是否为击键,HTTP响应,或者间隔定时器,监听值和停止监听的接口是一样的。

由于这些优点,可观察对象在Angular中被广泛使用,也推荐用于应用程序开发。

有关更多信息,请参见

  • Angular Developer Guide - Observables

JavaScript中的observable

在过去,有一种将observable添加到JavaScript的趋势。然而,当异步迭代器被添加到JavaScript中时,这一努力就落空了。

有关更多信息,请参见

  • MDN JavaScript Reference -for await...ofStatement

最新更新