我正在使用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),
)
))
)
)
答案 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)
),
)
))
)
)