如何防止Epic重复发起同一owner/url的并发请求?
解决Redux-Observable中避免重复并发请求的问题
嘿,我完全get到你的需求了——要在触发REMOTE_DATA_STARTED action时,基于action.url和action.owner获取数据,同时绝对不能对同一个owner/url组合发起两个并发请求,而且不需要取消正在进行的请求,只要阻止新请求启动就行。你提到的exhaustMap确实是核心,再搭配groupBy就能完美解决这个问题,我给你详细拆解下:
核心思路
我们需要把请求按owner/url的唯一组合分组,让每个组合的请求单独排队处理:
groupBy:把所有REMOTE_DATA_STARTEDaction按owner+url的唯一标识分组,确保相同组合的请求进入同一个处理流。exhaustMap:在每个分组流里,只要当前请求还在进行中,就忽略新进来的action;只有当当前请求完成(成功或失败)后,才会处理下一个action。这样既不会取消现有请求,又能完全阻止重复并发请求。
完整代码示例
import { ofType } from 'redux-observable'; import { groupBy, exhaustMap, mergeMap, catchError } from 'rxjs/operators'; import { of } from 'rxjs'; // 假设你已经定义了这些action类型 import { REMOTE_DATA_STARTED, REMOTE_DATA_SUCCESS, REMOTE_DATA_FAILURE } from './your-action-types'; const fetchRemoteDataEpic = (action$) => { return action$.pipe( // 过滤出我们需要处理的action ofType(REMOTE_DATA_STARTED), // 按owner和url的组合分组,生成唯一键确保分组准确 groupBy(action => `${action.owner}_${action.url}`), // 合并每个分组的处理结果 mergeMap(requestGroup$ => { return requestGroup$.pipe( // exhaustMap:忽略新action直到当前请求完成 exhaustMap(action => { // 这里替换成你的实际数据请求逻辑(axios/fetch都可以) return fetch(`/api/data?owner=${action.owner}&resource=${action.url}`) .then(res => res.json()) .then(data => ({ type: REMOTE_DATA_SUCCESS, payload: data, owner: action.owner, url: action.url })) .catch(err => of({ type: REMOTE_DATA_FAILURE, payload: err.message, owner: action.owner, url: action.url })); }) ); }) ); };
关键细节解释
- 分组键的选择:用
${action.owner}_${action.url}生成唯一键是最简单可靠的方式,如果你担心字符串拼接有冲突,也可以用JSON.stringify({owner: action.owner, url: action.url}),只要保证相同组合能生成相同的键就行。 - 错误处理:一定要在请求里加上
catchError,不然单个请求失败会导致整个分组流中断,后续的请求就无法处理了。用of()把错误转换成失败action,能确保流一直保持活跃。 - 为什么不用
switchMap:switchMap会取消当前正在进行的请求,这和你“不需要取消现有请求”的需求不符;而exhaustMap正好是忽略新请求,完全符合你的要求。
内容的提问来源于stack exchange,提问作者Sylvain
相关产品推荐
相关产品推荐

