在`toArray`之前发出动作-Redux可观察到

时间:2018-09-25 18:52:17

标签: rxjs rxjs5 redux-observable rxjs6 rxjs-pipeable-operators

我正在使用Redux Observable,并且在触发史诗动作时需要解决计时问题。

我要遍历一系列项目,以便在每个项目上进行AJAX调用。收到AJAX响应后,我想立即执行一些操作。在针对原始数组中的每个项目返回所有AJAX响应之后,我想触发更多操作。

即使原始数组尚未完成循环,如何在timer到期后立即执行这些操作?

const someEpic = action$ => (
    action$
    .pipe(
        ofType(SOME_ACTION),
        switchMap(({ payload }) => (
            from(payload) // This is an array of items
            .pipe(
                mergeMap(() => (
                    timer(5000) // This is my AJAX call
                    .pipe(
                        map(loadedAction),
                    )
                )),
                toArray(),
                map(anotherAction),
            )
        ))
    )
)

2 个答案:

答案 0 :(得分:1)

最简单的方法实际上是使用tap随处发出动作。这是假设您有权访问商店。例如:

tap(result => this.store.dispatch(...))

不过,一种更“ Rx”的方式是使用multicast拆分链,然后立即释放一部分(这是加载进度),而另一半使用toArray()进行链接以收集所有结果然后变成另一个信号,表明加载已完成。

import { range, Subject, of } from 'rxjs';
import { multicast, delay, merge, concatMap, map, toArray } from 'rxjs/operators';

const process = v => of(v).pipe(
  delay(1000),
  map(p => `processed: ${p}`),
);

range(1, 5)
  .pipe(
    concatMap(v => process(v)),
    multicast(
      () => new Subject(), 
      s => s.pipe(
        merge(s.pipe(toArray()))
      )
    ),
  )
  .subscribe(console.log);

实时演示:https://stackblitz.com/edit/rxjs6-demo-k9hwtu?file=index.ts

答案 1 :(得分:0)

这需要2个史诗。

const ajaxCallerEpic = action$ => (
    action$
    .pipe(
        ofType(AJAX_ACTION),
        switchMap(({
            payload,
            payloadId,
        }) => (
            merge(
                from(payload) // This is an array of items
                .pipe(
                    mergeMap(() => (
                        timer(5000) // This is my AJAX call
                        .pipe(
                            switchMap(() => (
                                merge(
                                    of(loadedAction),
                                    of(
                                        sentData(
                                            payloadId
                                        )
                                    ),
                                )
                            )),
                        )
                    )),
                ),
            )
        ))
    )
)

const ajaxResponsesEpic = action$ => (
    action$
    .pipe(
        ofType(AJAX_ACTION),
        switchMap(({
            payload,
            payloadId,
        }) => (
            action$
            .pipe(
                ofType(SENT_DATA_ACTION),
                filter(({ id }) => (
                    id === payloadId
                )),
                bufferCount(
                    payload
                    .length
                ),
                map(anotherAction),
            )
        ))
    )
)

重要的是第二个SENT_DATA_ACTION。调用它时,我传递了一个唯一的ID,以确保您正在收听正确的ID。如果您不发送所有消息,则只要打开浏览器,它就会一直处于监听状态。您总是可以在内部侦听器上添加超时,以确保其完成。另一个问题是,如果ajaxResponsesEpic设置action$侦听器的时间晚于ajaxCallerEpic运行AJAX调用的时间。

它很可能最终成为race条件。为了解决这些问题,您需要首先执行ajaxResponsesEpic,以便它设置动作侦听器,同时在侦听之后启动AJAX调用。

像这样:

const ajaxCallerEpic = action$ => (
    action$
    .pipe(
        ofType(AJAX_READY_ACTION),
        switchMap(({
            payload,
            payloadId,
        }) => (
            merge(
                from(payload) // This is an array of items
                .pipe(
                    mergeMap(() => (
                        timer(5000) // This is my AJAX call
                        .pipe(
                            switchMap(() => (
                                merge(
                                    of(loadedAction),
                                    of(
                                        sentAjaxData(
                                            payloadId
                                        )
                                    ),
                                )
                            )),
                        )
                    )),
                ),
            )
        ))
    )
)

const ajaxResponsesEpic = action$ => (
    action$
    .pipe(
        ofType(AJAX_ACTION),
        switchMap(({
            payload,
            payloadId,
        }) => (
            merge(
                (
                    action$
                    .pipe(
                        ofType(SENT_AJAX_DATA_ACTION),
                        filter(({ id }) => (
                            id === payloadId
                        )),
                        bufferCount(
                            payload
                            .length
                        ),
                        map(anotherAction),
                    )
                ),
                (
                    of(ajaxReadyAction)
                ),
            )
        ))
    )
)