优雅实现并行接口调用的RxJS神器:forkJoin

305 阅读3分钟

前言

我在开发公司项目的时候,有这样一个页面的下拉框数组,是公司内七八个部门的同事数据组合而成。

于是我在最开始的时候,就通过调用获取某个指定部门ID下所有同事接口来获取数据,并通过一个常量数组一个一个push进去,例如:

// 获取运营人员下拉
this.devMangeService.getUserList('23').subscribe(res => {
    const data = res['data'];
    data.forEach(index => {
        this.yyList.push(index);
    });
});
this.devMangeService.getUserList('39').subscribe(res => {
    const data = res['data'];
    data.forEach(index => {
        this.yyList.push(index);
    });
});
// ……省略中间7个
this.devMangeService.getUserList('107').subscribe(res => {
    const data = res['data'];
    data.forEach(index => {
        this.yyList.push(index);
    });
});

是不是头都看大了?

这是我刚入职的时候写的代码(比较笨比),其实通过this.yyList=[...res['data']]会更简洁易用一些

但你知道的,我的代码洁癖不允许我有如此臃肿且重复的代码,这样不优雅!

于是我便去查了查资料,试图找到一种能够将代码精简化的方式,没想到还真有。

那就是RxJS操作符:forkJoin

forkJoin是什么

官方定义

Accepts an Array of ObservableInput or a dictionary Object of ObservableInput and returns an Observable that emits either an array of values in the exact same order as the passed array, or a dictionary of values in the same shape as the passed dictionary.

——RxJS官方文档:forkJoin

通俗定义

简单来说,forkJoin 是 RxJS 中的一个操作符,用于并行执行多个Observable,并在所有 Observable都完成后,以数组的形式返回最终值。

也就是说,当你有重复执行Observable等待多个Observable都执行完成才进行下一步的时候,就可以使用forkJoin 操作符了。对于我这样要调用多个查询ID的接口的使用场景实在是太合适了。

除了RxJS,JAVA中也有forkJoin的存在,用于实现高并发的使用场景,在这里不多赘述。

具体实现

信息集合

我所执行的Observable中,只有部门ID是不同的,那么我便可以将部门ID整合为一个数组,用于并行调用:

const ids = ['23', '39', '41', '67', '73', '95', '101', '103', '105', '107'];

创建请求列表

目前已经有了需要查询的部门ID,那就可以为每个ID创建一个Observable请求,使用map函数生成一个请求列表:

const requests = ids.map((id) => this.devemange.getUserList(id));

使用forkJoin并行处理

接下来使用今天的主角:forkJoin,来执行我们已经写好的请求列表。

这次我就使用扩展运算符来代替push方法,使得代码更加精简

forkJoin(requests).subscribe(responses => {
  responses.forEach((res:any) => {
    this.yyList.push(...res['data']); // 使用扩展运算符
  });
});

到这一步,我就通过使用forkJoin操作符,生成了一段精简、易维护、性能提升且优雅的代码获取了我想要的yyList数组。

另一个使用场景

由于我上述的使用场景是重复执行Observable,缺少了等待多个Observable都执行完成才进行下一步该场景,于是我写了一个简单的demo来演示一下:


import { Component, OnInit } from '@angular/core';
import { forkJoin } from 'rxjs';
import { DataService } from '../../services/data.service';
​
@Component({
  selector: 'demo',
})
export class DemoComponent implements OnInit {
    
  constructor(private dataService: DataService) {}
​
  ngOnInit() {
    // 使用 forkJoin 聚合请求
    forkJoin({
      user: this.dataService.getUserData(),
      orders: this.dataService.getOrderData(),
      inventory: this.dataService.getInventoryData(),
      sales: this.dataService.getSalesData(),
      analytics: this.dataService.getAnalyticsData(),
    }).subscribe({
      next: (results) => {
        // 获取results结果
        console.log('获取数据:', results);
      },
      error: (err) => console.error('失败:', err),
      complete: () => {
        console.log('整个流程完成');
      },
    });
  }
}