RxJS 中的 combineLatest 和 withLatestFrom 详解

RxJS 是一种函数式响应式编程库,广泛用于前端开发中。在 RxJS 中,有两种常用的操作符:combineLatest 和 withLatestFrom。本文将详细介绍这两种操作符的区别、使用方法和实际应用场景。

combineLatest

combineLatest 是 RxJS 中的一个操作符,它将多个 Observable 对象的值组合成一个数组,并在每个 Observable 发出新值时重新计算。当多个 Observable 对象中的值都发生变化时,combineLatest 会触发并发出一个新的数组。

combineLatest 的使用方法如下所示:

combineLatest(obs1, obs2, obs3, ..., (val1, val2, val3, ...) => {...})

其中,obs1、obs2、obs3 等都是 Observable 对象,用于组合成一个新的 Observable 对象。最后一个参数是一个回调函数,用于处理组合后的值。

import { interval, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

const source1$ = interval(1000);
const source2$ = interval(500);

const combined$ = combineLatest(source1$, source2$).pipe(
  map(([val1, val2]) => val1 + val2)
);

combined$.subscribe(console.log);

上面的代码中,通过使用 interval 创建了两个 Observable 对象:source1$ 每隔 1 秒发射一次值,source2$ 每隔 0.5 秒发射一次值。使用 combineLatest 将这两个 Observable 对象组合,然后使用 map 操作符将它们加起来。最后,通过 subscribe 订阅了这个新的 Observable 对象 combined$,并将结果打印在控制台上。

withLatestFrom

withLatestFrom 也是 RxJS 中的一个操作符,它将多个 Observable 对象的值组合成一个新的 Observable 对象,并在第一个 Observable 发出新值时重新计算。当第一个 Observable 对象发出新值时,withLatestFrom 会触发并发出一个新的值,并使用其他 Observable 对象的最新值来计算。

withLatestFrom 的使用方法如下所示:

withLatestFrom(obs1, obs2, obs3, ..., (val1, val2, val3, ...) => {...})

其中,obs1、obs2、obs3 等都是 Observable 对象,用于与第一个 Observable 对象组合成一个新的 Observable 对象。最后一个参数是一个回调函数,用于处理组合后的值。

import { interval, fromEvent } from 'rxjs';
import { withLatestFrom, map } from 'rxjs/operators';

const source1$ = interval(1000);
const source2$ = fromEvent(document, 'click');

const combined$ = source1$.pipe(
  withLatestFrom(source2$),
  map(([val1, event]) => `Interval value: ${val1}, 
                            Last click at X: ${event.clientX}, 
                            Last click at Y: ${event.clientY}`)
);

combined$.subscribe(console.log);

上面的代码中,通过使用 interval 创建了一个 Observable 对象 source1$,它每隔 1 秒发射一次值。同时,通过使用 fromEvent 创建了另一个 Observable 对象 source2$,它会在每次鼠标点击事件发生时发射一个事件对象。然后,在 source1$ 上使用 withLatestFrom,将 source1$ 与 source2$ 组合成一个新的 Observable 对象 combined$。在组合后的返回值中,我们同时输出了 source1$ 的值和 source2$ 最后一次点击事件的 X 和 Y 坐标。

区别

虽然 combineLatest 和 withLatestFrom 操作符都将多个 Observable 对象的值组合成一个新的 Observable 对象,但它们之间有一个重要的区别:触发条件不同。

  • 对于 combineLatest,只要两个或多个 Observable 对象中任意一个发出新值,就会重新计算。
  • 对于 withLatestFrom,只有第一个 Observable 对象发出新值时,才会重新计算,并使用其他 Observable 对象的最新值来计算。

在实际应用中,这个区别非常重要。通过理解这个区别,我们可以更好地选择使用合适的操作符,以提高代码质量。

应用场景

combineLatest 和 withLatestFrom 都是非常实用的操作符,在前端开发中有着广泛的应用场景。下面是它们的几个典型用途:

combineLatest 的应用场景

  • 当需要同时监听多个 Observable 对象,并在它们任意一个发生变化时进行处理时,可以使用 combineLatest。

  • 当需要将多个 Observable 对象的值合并成一个新的值时,可以使用 combineLatest。

withLatestFrom 的应用场景

  • 当需要根据多个 Observable 对象中的最新值来计算一个新的值时,可以使用 withLatestFrom。

  • 当需要多个 Observable 对象中的值来更新另一个 Observable 对象的值时,也可以使用 withLatestFrom。

下面是一些具体的例子:

combineLatest 的应用场景

  1. 同时监听表单中多个输入框的变化,并将它们的合法性进行校验。
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

const input1$ = document.getElementById('input1').valueChanges;
const input2$ = document.getElementById('input2').valueChanges;
const input3$ = document.getElementById('input3').valueChanges;

const combined$ = combineLatest(input1$, input2$, input3$).pipe(
  map(([val1, val2, val3]) => {
    const validation1 = isInput1Valid(val1);
    const validation2 = isInput2Valid(val2);
    const validation3 = isInput3Valid(val3);
    return validation1 && validation2 && validation3;
  })
);

combined$.subscribe(isValid => {
  document.getElementById('submitBtn').disabled = !isValid;
});

在这个例子中,我们同时监听了表单中的多个输入框,使用 combineLatest 将它们的值组合并每当其发生变化时校验它们的合法性。最后,我们根据校验结果来禁用或启用提交按钮。

  1. 根据用户选择的两个下拉框的值,动态生成一个弹窗标题。
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

const select1$ = document.getElementById('select1').valueChanges;
const select2$ = document.getElementById('select2').valueChanges;

const combined$ = combineLatest(select1$, select2$).pipe(
  map(([val1, val2]) => `You've selected ${val1} and ${val2}`)
);

combined$.subscribe(title => {
  showModal(title);
});

在这个例子中,我们使用 combineLatest 监听两个下拉框的选择值,并将它们的值组合到一起来生成一个弹窗标题。当它们的值发生变化时,我们重新使用组合后的值来更新弹窗标题。

withLatestFrom 的应用场景

  1. 当用户输入了一个新的值时,根据上一个值和当前值的变化来判断输入框中的值是否有效。
import { fromEvent } from 'rxjs';
import { pipe, withLatestFrom, map, filter } from 'rxjs/operators';

const input$ = document.getElementById('input').valueChanges;

const isValid$ = input$.pipe(
  withLatestFrom(input$, (prev, curr) => [prev, curr]),
  filter(([prev, curr]) => isInputValid(prev, curr)),
  map(([prev, curr]) => curr)
);

isValid$.subscribe(val => {
  console.log(`The latest valid value is ${val}`);
});

在这个例子中,我们通过使用 withLatestFrom 和 filter 两个操作符,实现了在用户每次输入新的值时,检测上一次的值和当前值的变化,判断输入框中的值是否有效,并将有效的值推送给下游的 Observable 对象。

  1. 根据用户的滚动行为和屏幕宽度,实现一个滚动导航条的样式变化。
import { fromEvent, combineLatest } from 'rxjs';
import { map, filter } from 'rxjs/operators';

const scroll$ = fromEvent(window, 'scroll');
const resize$ = fromEvent(window, 'resize');

const minScreenSize = 768;

const combined$ = combineLatest(scroll$, resize$).pipe(
  filter(([_, event]) => event.target.innerWidth < minScreenSize),
  map(([_, event]) => event.target.scrollY),
  map(scrollY => scrollY > 100 ? 'dark' : 'light')
);

combined$.subscribe(theme => {
  document.getElementById('navbar').classList.remove('light', 'dark');
  document.getElementById('navbar').classList.add(theme);
});

在这个例子中,我们使用 combineLatest 监听了全局窗口的滚动和大小改变事件,然后使用 filter 和 map 操作符处理这些值。最后,我们使用最新的值来判断当前屏幕上方在哪个位置,并根据此位置更改导航条的主题样式。

总结

通过本文的介绍,我们了解了 RxJS 中两种重要的操作符:combineLatest 和 withLatestFrom。它们都非常实用,在前端开发中有着广泛的应用场景。我们将它们的区别、使用方法和实际应用场景都进行了详细的介绍。希望读者能够掌握它们的使用技巧,以便在实际项目中能够更好地运用这些操作符。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65948eb8eb4cecbf2d8e8b16


纠错反馈