如何最好地使用rxjs在单个操作中执行多个获取操作,使用forEach方法?

huangapple go评论57阅读模式
英文:

How can I best use rxjs to perform multiple fetch operations in a single action using a forEach approach

问题

让我解释一下您需要实现的语法

首先您需要使用`frameworks`对象来迭代每个框架并对每个框架执行相应的API调用

在这种情况下您可以使用`Object.keys`来获取`frameworks`对象中所有框架的键即框架的名称的数组

然后您可以使用`Array.prototype.map`方法来将每个框架的键映射到相应的API调用

最后您可以使用`forkJoin`来同时处理所有API调用以确保在所有调用都完成后再继续

以下是相应的代码示例

```js
let fetchHabitatsEpic = (action$, state$) => action$.pipe(
  ofType(
    mapperActions.initialise.type,
  ),
  switchMap(() => {
    const frameworkKeys = Object.keys(frameworks);

    const apiCalls = frameworkKeys.map(key => {
      const framework = frameworks[key];
      return fetchHabitats(framework).pipe(
        map(result => mapperActions.fetchHabitatsCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))
      );
    });

    return concat(
      of(globalActions.startLoading('habitats')),
      forkJoin(apiCalls).pipe(
        map(() => globalActions.stopLoading('habitats')),
        catchError(e => of(globalActions.errorOccurred(e.message)))
      )
    );
  })
);

在此示例中,frameworkKeysframeworks对象中所有框架的键的数组。然后,我们使用map方法将每个框架的键映射到相应的API调用,并将所有调用放入apiCalls数组中。

接着,我们使用forkJoin来同时处理所有API调用。一旦所有调用都完成,它会返回一个数组,其中包含每个API调用的结果。最后,我们在concat中包含了开始和结束加载的动作。

这样,您就可以使用单个动作触发所有四个框架的API调用,并在所有调用完成后停止加载。

英文:

I am new to React and tasked with modifying an existing application which already performs multiple api calls. The pattern that is in use in the app is demonstrated by the below example. EG if an action mapperActions.selectPolygon of type CaseReducerActions<CaseReducers> is raised, then the below code will execute, calling the fetchPolygon function to retrieve data asynchronously from an AWS lambda and the returned result will be processed by the fetchPolygonCompleted function.

let fetchPolygonStatsEpic = (action$: any, state$: StateObservable<RootState>) => action$.pipe(
  ofType(
    mapperActions.selectPolygon.type,
    mapperActions.alterQueryIndexname.type,
  ),
  filter(()  => state$.value.mapper.selectedPolygon !== undefined),
  switchMap(() =>
    concat(
      of(globalActions.startLoading('polygon')),
      fetchPolygon(state$.value.mapper).pipe(
        map(result => mapperActions.fetchPolygonCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))),
      of(globalActions.stopLoading('polygon')),
    )
  )
)

The change I need to implement is as follows:

I have a type 'Framework'...

export type Framework = {
....
}

...a collection of these...

export const frameworks: {[framework: string]: Framework} = {
  liveng0: {
    ...
  },
  liveng1: {
    ...
  },
  habmosCairngorms: {
    ...
  },
  spaceintCairngorms: {
    ...
  }
}

..and an API method that retrieves data for a specified Framework...

export let fetchHabitats = (requiredFramework: Framework): Observable<FrameworkHabitats> => {
...
}

When the action mapperActions.initialise of type CaseReducerActions<CaseReducers> is raised, I need to execute a block of code which will execute and process 4 separate asynchronous API calls to fetch the data for each of the 4 framework types above in turn.

A naive approach of simply copy-and-pasting the API call for each framework in turn within the app's establised pattern for fetching data actually works correctly and produces the results I require, ie:

let fetchHabitatsEpic = (action$: any, state$: StateObservable<RootState>) => action$.pipe(
  ofType(
    mapperActions.initialise.type,
  ),
  switchMap(() =>
    concat(
      of(globalActions.startLoading('habitats')),
      fetchHabitats(frameworks.liveng0).pipe(
        map(result => mapperActions.fetchHabitatsCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))),
      fetchHabitats(frameworks.liveng1).pipe(
        map(result => mapperActions.fetchHabitatsCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))),
      fetchHabitats(frameworks.habmosCairngorms).pipe(
        map(result => mapperActions.fetchHabitatsCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))),
      fetchHabitats(frameworks.spaceintCairngorms).pipe(
        map(result => mapperActions.fetchHabitatsCompleted(result)),
        catchError(e => of(globalActions.errorOccurred(e.message)))),        
      of(globalActions.stopLoading('habitats')),
    )
  )
)

Although the above works as required, it clearly needs to be replaced with a forEach approach that operates over each Framework in turn and treats each API call atomically, whilst still being able to trigger all 4 from a single action. Could you please explain the syntax required to achieve this?

答案1

得分: 1

你可以使用Object.values()来迭代框架,然后使用Array.map()将它们映射到API调用,如下所示:

concat(
      of(globalActions.startLoading('habitats')),
      ...Object.values(frameworks).map(framework => 
        fetchHabitats(framework).pipe(
          map(result => mapperActions.fetchHabitatsCompleted(result)),
          catchError(e => of(globalActions.errorOccurred(e.message)))
        )
      )
)
英文:

You can use Object.values() to iterate over the frameworks and Array.map() to map them to API calls, as follows:

concat(
      of(globalActions.startLoading('habitats')),
      ...Object.values(frameworks).map(framework => 
        fetchHabitats(framework).pipe(
          map(result => mapperActions.fetchHabitatsCompleted(result)),
          catchError(e => of(globalActions.errorOccurred(e.message)))
        )
      )
)

huangapple
  • 本文由 发表于 2023年1月9日 18:50:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75056192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定