理解Angular RxJS映射数据操作

简介: Map 数据是程序开发时的一种常见操作。当在代码中使用RxJS来生成数据流时,很可能最终需要一种方法来将数据映射成需要的任何格式。RxJS提供了常规的 map 函数,还有 mergeMap、switchMap和concatMap这样的函数,它们的处理方式略有不同。

Map 数据是程序开发时的一种常见操作。当在代码中使用RxJS来生成数据流时,很可能最终需要一种方法来将数据映射成需要的任何格式。RxJS提供了常规的 map 函数,还有 mergeMapswitchMapconcatMap这样的函数,它们的处理方式略有不同。

map

map操作符是最常见的。对于Observable发出的每个值,都可以应用一个函数来修改数据。返回值将在后台被重新释放为Observable,这样就可以在流中继续使用它。它的工作原理与在数组中使用它的方法非常相似。

不同之处在于,数组将始终只是数组,而在映射时,将获得数组中当前的索引值。对于observable,数据的类型可以是各种类型。这意味着可能需要在 Observable map 函数中做一些额外的操作来获得想要的结果。看下面的例子::

import { of } from "rxjs";
import { map } from "rxjs/operators";
// 创建数据
const data = of([
    {
        brand: "保时捷",
        model: "911"
    },
    {
        brand: "保时捷",
        model: "macan"
    },
    {
        brand: "法拉利",
        model: "458"
    },
    {
        brand: "兰博基尼",
        model: "urus"
    }
]);
// 按照brand model的格式输出,结果:["保时捷 911", "保时捷 macan", "法拉利 458", "兰博基尼 urus"]
data.pipe(map(cars => cars.map(car => `${car.brand} ${car.model}`))).subscribe(cars => console.log(cars));
// 过滤数据,只保留brand为porsche的数据,结果:[{"brand":"保时捷","model":"911"},{"brand":"保时捷","model":"macan"}]
data.pipe(map(cars => cars.filter(car => car.brand === "保时捷"))).subscribe(cars => console.log(cars));

首先用一系列汽车创建了可观察对象。然后订阅这个可观测值2次。

  1. 第一次修改数据时,得到了一个由brandmodel字符串连接起来的数组。
  2. 第二次修改数据时,得到了一个只有brand保时捷的数组。

在这两个例子中,使用Observable map操作符来修改由Observable发出的数据。返回修改的结果,然后map操作符将结果封装到一个可观察对象中,以便后面可以subscribe

MergeMap

现在假设有这样一个场景,有一个可观察到的对象,它发出一个数组,对于数组中的每一项,都需要从服务器获取数据。

可以通过订阅数组来做到这一点,然后设置一个映射来调用一个处理API调用的函数,订阅其结果。如下:

import { of, from } from "rxjs";
import { map, delay } from "rxjs/operators";
const getData = param => {
    return of(`检索参数: ${param}`).pipe(delay(1000));
};
from([1, 2, 3, 4])
    .pipe(map(param => getData(param)))
    .subscribe(val => console.log(val));

map函数返回getData函数的值。在这种情况下,这是可观测的。但这产生了一个问题:因为现在要处理一个额外的可观测值。

为了进一步阐明这一点:from([1,2,3,4])作为“外部”可观察对象,getData()的结果作为“内部”可观察对象。从理论上讲,必须同时接受外部和内部的可观测数据。可以是这样的:

import { of, from } from "rxjs";
import { map, delay } from "rxjs/operators";
const getData = param => {
    return of(`检索参数: ${param}`).pipe(delay(1000));
};
from([1, 2, 3, 4])
    .pipe(map(param => getData(param)))
    .subscribe(val => val.subscribe(data => console.log(data)));

可以想象,这与必须调用Subscribe两次的理想情况相去甚远。这就是mergeMap发挥作用的地方。MergeMap本质上是mergeAllmap的组合。MergeAll负责订阅“内部”可观察对象,当MergeAll将“内部”可观察对象的值合并为“外部”可观察对象时,不再需要订阅两次。如下:

import { of, from } from "rxjs";
import { map, delay, mergeAll } from "rxjs/operators";
const getData = param => {
    return of(`检索参数: ${param}`).pipe(delay(1000));
};
from([1, 2, 3, 4])
    .pipe(
        map(param => getData(param)),
        mergeAll()
    )
    .subscribe(val => console.log(val));

这已经好多了,mergeMap将是这个问题的最佳解决方案。下面是完整的例子:

import { of, from } from "rxjs";
import { map, mergeMap, delay, mergeAll } from "rxjs/operators";
const getData = param => {
    return of(`检索参数: ${param}`).pipe(delay(1000));
};
// 使用 map
from([1, 2, 3, 4])
    .pipe(map(param => getData(param)))
    .subscribe(val => val.subscribe(data => console.log(data)));
// 使用 map 和 mergeAll
from([1, 2, 3, 4])
    .pipe(
        map(param => getData(param)),
        mergeAll()
    )
    .subscribe(val => console.log(val));
// 使用 mergeMap
from([1, 2, 3, 4])
    .pipe(mergeMap(param => getData(param)))
    .subscribe(val => console.log(val));

SwitchMap

SwitchMap具有类似的行为,它也将订阅内部可观察对象。然而,switchMapswitchAllmap的组合。SwitchAll取消先前的订阅并订阅新订阅。在上面的场景中,想要为“外部”可观察对象数组中的每一项执行API调用,但switchMap并不能很好地工作,因为它将取消前3个订阅,只处理最后一个订阅。这意味着只会得到一个结果。完整的例子可以在这里看到:

import { of, from } from "rxjs";
import { map, delay, switchAll, switchMap } from "rxjs/operators";
const getData = param => {
    return of(`retrieved new data with param ${param}`).pipe(delay(1000));
};
// 使用 a regular map
from([1, 2, 3, 4])
    .pipe(map(param => getData(param)))
    .subscribe(val => val.subscribe(data => console.log(data)));
// 使用 map and switchAll
from([1, 2, 3, 4])
    .pipe(
        map(param => getData(param)),
        switchAll()
    )
    .subscribe(val => console.log(val));
// 使用 switchMap
from([1, 2, 3, 4])
    .pipe(switchMap(param => getData(param)))
    .subscribe(val => console.log(val));

虽然switchMap不适用于当前的场景,但它适用于其他场景。例如,如果将筛选器列表组合到数据流中,并在更改筛选器时执行API调用,那么它将派上用场。如果先前的筛选器更改仍在处理中,而新的更改已经完成,那么它将取消先前的订阅,并在最新的更改上启动新的订阅。这里可以看到一个例子:

import { of, from, BehaviorSubject } from "rxjs";
import { map, delay, switchAll, switchMap } from "rxjs/operators";
const filters = ["brand=porsche", "model=911", "horsepower=389", "color=red"];
const activeFilters = new BehaviorSubject("");
const getData = params => {
    return of(`接收参数: ${params}`).pipe(delay(1000));
};
const applyFilters = () => {
    filters.forEach((filter, index) => {
        let newFilters = activeFilters.value;
        if (index === 0) {
            newFilters = `?${filter}`;
        } else {
            newFilters = `${newFilters}&${filter}`;
        }
        activeFilters.next(newFilters);
    });
};
// 使用 switchMap
activeFilters.pipe(switchMap(param => getData(param))).subscribe(val => console.log(val));
applyFilters();

正如在控制台中看到的,getData只记录一次所有参数。节省了3次API的调用。

ConcatMap

最后一个例子是concatMapconcatMap订阅了内部可观察对象。但与switchMap不同的是,如果有一个新的观察对象进来,它将取消当前观察对象的订阅,concatMap在当前观察对象完成之前不会订阅下一个观察对象。这样做的好处是保持了可观测对象发出信号的顺序。为了演示这个:

import { of, from } from "rxjs";
import { map, delay, mergeMap, concatMap } from "rxjs/operators";
const getData = param => {
    const delayTime = Math.floor(Math.random() * 10000) + 1;
    return of(`接收参数: ${param} and delay: ${delayTime}`).pipe(delay(delayTime));
};
// 使用map
from([1, 2, 3, 4])
    .pipe(map(param => getData(param)))
    .subscribe(val => val.subscribe(data => console.log("map:", data)));
// 使用mergeMap
from([1, 2, 3, 4])
    .pipe(mergeMap(param => getData(param)))
    .subscribe(val => console.log("mergeMap:", val));
// 使用concatMap
from([1, 2, 3, 4])
    .pipe(concatMap(param => getData(param)))
    .subscribe(val => console.log("concatMap:", val));

getData函数的随机延迟在1到10000毫秒之间。通过浏览器日志,可以看到mapmergeMap操作符将记录返回的任何值,而不遵循原始顺序。concatMap记录的值与它们开始时的值相同。

总结

将数据映射到所需的格式是一项常见的任务。RxJS附带了一些非常简洁的操作符,可以很好的完成这项工作。

概括一下:map用于将normal值映射为所需的任何格式。返回值将再次包装在一个可观察对象中,因此可以在数据流中继续使用它。当必须处理一个“内部”观察对象时,使用mergeMapswitchMapconcatMap更容易。如果只是想将数据转成Observable对象,使用mergeMap;如果需要丢弃旧的Observable对象,保留最新的Observable对象,使用switchMap;如果需要将数据转成Observable对象,并且需要保持顺序,则使用concatMap


相关文章
|
15天前
|
缓存 前端开发 JavaScript
前端serverless探索之组件单独部署时,利用rxjs实现业务状态与vue-react-angular等框架的响应式状态映射
本文深入探讨了如何将RxJS与Vue、React、Angular三大前端框架进行集成,通过抽象出辅助方法`useRx`和`pushPipe`,实现跨框架的状态管理。具体介绍了各框架的响应式机制,展示了如何将RxJS的Observable对象转化为框架的响应式数据,并通过示例代码演示了使用方法。此外,还讨论了全局状态源与WebComponent的部署优化,以及一些实践中的改进点。这些方法不仅简化了异步编程,还提升了代码的可读性和可维护性。
|
6月前
|
JavaScript 前端开发 架构师
Angular进阶:理解RxJS在Angular应用中的高效运用
RxJS(Reactive Extensions for JavaScript)是JavaScript的一个响应式编程库,特别适用于处理异步数据流。
81 0
|
3月前
|
开发者 开发工具 UED
JSF应用的社交革命:一键解锁社交媒体超级功能,引爆用户参与度的奇迹!
【8月更文挑战第31天】本文探讨了在JavaServer Faces (JSF)应用中集成社交媒体功能的最佳实践,包括选择合适的API和SDK、示例代码及实现细节。通过集成社交媒体,应用能提供即时内容分享、互动交流和个性化体验,提升用户参与度。文章还强调了用户体验优化、安全性及隐私保护的重要性,并总结了社交媒体集成对企业竞争优势的意义。
41 0
|
前端开发 JavaScript API
Angular与Rxjs学习
Angular与Rxjs学习
152 0
Angular与Rxjs学习
Angular 结合 rxjs 实现拖拽
在上两篇文章中,我们学习了元素中必知重要属性和方法和 Angular 中自定义 Video 操作,没有度过的读者可先了解。那么,现在有这么一个需求,你会怎么实现呢?
Angular 结合 rxjs 实现拖拽
升级到Angular6后对老版本的RXJS代码做相应的调整
还没有了解过RXJS6的童鞋,可以查看我的另外一篇博文,此篇博文主要是对于RXJS5升级到RXJS6的代码调整示例 RXJS5版本 在RXJS5上我们是这样写请求的 import 'rxjs/add/observable/of'; import 'rxjs/add/observable/thr...
1432 0
|
3月前
|
API 开发者 UED
PrimeFaces:JSF的魔法衣橱,解锁UI设计的无限可能!
【8月更文挑战第31天】本文介绍如何结合 JSF(JavaServer Faces)和 PrimeFaces 构建美观且功能强大的现代用户界面。PrimeFaces 提供丰富的 UI 组件库,包括按钮、输入框、数据网格等,支持现代 Web 标准,简化界面开发。文章通过具体示例展示如何使用 `<p:inputText>` 和 `<p:calendar>` 等组件创建用户表单,并用 `<p:dataTable>` 展示数据集合,提升 JSF 应用的易用性和开发效率。
56 0
|
3月前
|
开发者 安全 SQL
JSF安全卫士:打造铜墙铁壁,抵御Web攻击的钢铁防线!
【8月更文挑战第31天】在构建Web应用时,安全性至关重要。JavaServer Faces (JSF)作为流行的Java Web框架,需防范如XSS、CSRF及SQL注入等攻击。本文详细介绍了如何在JSF应用中实施安全措施,包括严格验证用户输入、使用安全编码实践、实施内容安全策略(CSP)及使用CSRF tokens等。通过示例代码和最佳实践,帮助开发者构建更安全的应用,保护用户数据和系统资源。
51 0
|
3月前
|
开发者 C# C++
揭秘:如何轻松驾驭Uno Platform,用C#和XAML打造跨平台神器——一步步打造你的高性能WebAssembly应用!
【8月更文挑战第31天】Uno Platform 是一个跨平台应用程序框架,支持使用 C# 和 XAML 创建多平台应用,包括 Web。通过编译为 WebAssembly,Uno Platform 可实现在 Web 上运行高性能、接近原生体验的应用。本文介绍如何构建高效的 WebAssembly 应用:首先确保安装最新版本的 Visual Studio 或 VS Code 并配置 Uno Platform 开发环境;接着创建新的 Uno Platform 项目;然后通过安装工具链并使用 Uno WebAssembly CLI 编译应用;最后添加示例代码并测试应用。
96 0
|
3月前
|
前端开发 开发者 安全
JSF支付功能大揭秘:探索如何在Java世界中实现安全无缝的在线支付体验
【8月更文挑战第31天】在电子商务和在线交易日益普及的今天,实现在线支付功能已成为许多Web应用的必备需求。JavaServer Faces (JSF) 作为一种流行的Java Web框架,提供了丰富的组件和工具来构建用户界面,包括与支付网关集成以实现在线支付。支付网关是处理信用卡和借记卡支付的系统,在商家和银行之间起到桥梁作用。本文将探讨如何使用JSF与支付网关集成,以及实现在线支付功能时需要考虑的关键点
44 0