【问题标题】:How to chain action observables to emit synchronously and then emit a final value如何链接动作可观察对象以同步发出,然后发出最终值
【发布时间】:2020-04-27 15:36:27
【问题描述】:

我有一个方法,它接受一个 observable 作为参数并返回另一个 observable(如果你对 Redux-Observable 有足够的了解,它有这个类型签名function (action$: Observable<Action>): Observable<Action>;

在该方法中,我需要 observable 在收到'POST_AND_GET_REQUEST' 操作时执行以下操作:

  1. 使用操作的有效负载部分为数组中的每个对象同步执行 POST 请求
  2. 这些同步调用完成后,执行 GET 请求
  3. 当GET请求返回响应时,则返回'POST_AND_GET_SUCCESS'动作

我创建了一个 stackblitz 来说明我目前所拥有的:https://stackblitz.com/edit/rxjs-a2pwb8?file=index.ts

但这里的代码 sn-p 也是如此:

import { from, of, Observable } from 'rxjs';
import { filter, delay, mapTo, mergeMap, concatMap, tap, switchMap } from 'rxjs/operators';

// GOAL
// synchronously post each period, when that's finished perform a get request
// postPeriods should only console the final POST_AND_GET_SUCCESS string that all was successful

interface ActionModel {
  type: string;
  payload: any;
}

const postPeriods = (action$: Observable<ActionModel>) => action$.pipe(
  filter(action => action.type === 'POST_AND_GET_REQUEST'),
  mergeMap(action =>
    from(action.payload).pipe(
      concatMap(period => of(`${period} post success`).pipe(
        delay(1000),
        tap(() => console.log(`${period} post success`)),
        // this is wrong - we only want this to emit once
        switchMap(response => of('get success').pipe(map => of('POST_AND_GET_SUCCESS')))
      ))
    ),
  )
);

const postRequestAction = from([{ type: 'POST_AND_GET_REQUEST', payload: ['period 1', 'period 2'] }]);

// this should be a single 'POST_AND_GET_SUCCESS'
postPeriods(postRequestAction).subscribe(val => console.log(val));

我知道switchMap 的位置是错误的,但我不知道如何链接from observable 或mergeMap 运算符以获得我想要的结果。

【问题讨论】:

  • 嗯..你是说switchMap语句中的动作只有在payload中的两个项目都完成后才能执行?
  • @wentjun 是的,没错。

标签: reactjs typescript rxjs observable redux-observable


【解决方案1】:

postPeriod 史诗中,我建议您通过返回单个可观察对象来结束操作,而不是返回多个可观察对象。

首先,您可以定义一个数组 (observableList),它由一系列可观察对象组成。然后,您可以使用来自 delayed 的 observables 推送数组。最后但同样重要的是,您移动 switchMap() 逻辑,使其仅在返回 observableList 时执行。

这将确保'get success' 操作仅在请求完成后执行一次,并返回可观察对象。

import { from, of, Observable } from 'rxjs';
import { filter, delay, mapTo, mergeMap, concatMap, tap, switchMap } from 'rxjs/operators';

// GOAL
// synchronously post each period, when that's finished perform a get request
// postPeriods should only console the final POST_AND_GET_SUCCESS string that all was successful

interface ActionModel {
  type: string;
  payload: any;
}

const postPeriods = (action$: Observable<ActionModel>) => action$.pipe(
  filter(action => action.type === 'POST_AND_GET_REQUEST'),
  mergeMap(action => {
    // replace the generics with a suitable typing
    const observableList: Observable<any>[] = [];

    const delayed = from(action.payload).pipe(
      concatMap(period => of(`${period} post success`)
        .pipe(
          delay(1000),
          tap(() => console.log(`${period} post success`)),
        )),
    )
    observableList.push(delayed);

    return observableList;
  }),
  switchMap(response => of('get success').pipe(map => of('POST_AND_GET_SUCCESS')))
);

const postRequestAction = from([{ type: 'POST_AND_GET_REQUEST', payload: ['period 1', 'period 2'] }]);

// this should be a single 'POST_AND_GET_SUCCESS'
postPeriods(postRequestAction).subscribe(val => {
  console.log('end:', val);
});

【讨论】:

  • 这里的想法很好,但我认为这种方法的问题是,如果你将内部的 observables 放入一个数组并返回它,它们也永远不会被订阅。当我将它插入到 stackblitz 中时,我看到了 POST_AND_GET_SUCCESS,但没有看到来自水龙头的 console.logs,我希望看到它以及最后一个 POST_AND_GET_SUCCESS。
【解决方案2】:

如果 GET 请求不需要之前 post 请求的响应中的任何数据,您可以使用 concat 在末尾添加 get 操作

...
mergeMap(action =>
  concat(
    from(action.payload).pipe(
      concatMap(period => of(`${period} post success`).pipe(
        delay(1000),
        tap(() => console.log(`${period} post success`)),
      ))
    ),
    of('get success').pipe(map => of('POST_AND_GET_SUCCESS'))
  )
)
...

【讨论】:

  • 这看起来像我想要的,但是当我将它插入 stackblitz 时出现错误:错误:您提供了 'function (source) { return source.lift.call(concat_1.concat .apply(void 0, [source].concat(observables))); }' 需要流的地方。您可以提供 Observable、Promise、Array 或 Iterable。
  • 你是从哪里导入“concat”的?应该是“rxjs”,而不是“rxjs/operators”!
  • 哦,我的错,当我需要从 rxjs 导入 concat 时,我将它作为 rxjs/operators 导入。
  • 哈,是的,刚刚想通了。这非常接近——当我取出水龙头时,它仍然从 concatMap 中得到值,但就我的目的而言,这很好。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-08
  • 1970-01-01
  • 2020-12-26
  • 1970-01-01
  • 2020-07-09
  • 2021-12-19
  • 1970-01-01
相关资源
最近更新 更多