首页 > 解决方案 > 处理 AJAX 请求时使用 Observables 的原因

问题描述

我已经开始使用响应式扩展 (RxJS),并且我了解当数据及时分布(或数据流)时使用 Observables 的强大功能。前 - 观察 mouseDown 事件等。但我很难在 API 调用的上下文中理解它们的用例

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

感谢帮助,谢谢。

标签: javascriptreactjsangularrxjs

解决方案


Observables 是 Promise 的超集。

如果你可以用 来做Promise,那么你可以用 来做Observable。您可能会坚持使用的一个原因PromisesObservableJavaScript 中没有内置的语法糖(异步等待)。

不建议将两者混合使用。没有性能或互操作性问题,但如果你无论如何都要使用 RxJS Observables,那么尽可能地坚持使用 Observables 是最清楚的(可维护性、可扩展性、可调试性)。

为什么要使用 Observables?

程序通常会随着时间的推移执行任务。您可以通过将数据视为一段时间内的流来抽象出一定级别的回调。

代替

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没有本地方法来取消正在进行的进程,因此您需要一个库来扩展 Promise 并提供一些额外的功能。这是您管理并发 Promise 所需的一项。

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

如果它们之间的距离超过一秒,则忽略按钮按下

使用 Promise,您可以设置一个变量(范围在您的回调函数之外),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 */
});

按下按钮开始每 500 毫秒轮询一次 API 端点

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

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 */
}

推荐阅读