史诗不会对另一个史诗的动作做出反应
Epic doesn't react on action from another epic
我对 redux-observables 有疑问。在我的情况下,一部史诗等待另一部史诗的结束。第二个史诗可以从缓存中发出请求或 return 数据。
当第二个请求按预期工作时,但是当它 return 缓存第一个时,第一个请求不会继续。
const { Observable } = Rx;
const FETCH_USER = 'FETCH_USER';
const FETCH_USER_FULFILLED = 'FETCH_USER_FULFILLED';
const FETCH_USER2 = 'FETCH_USER2';
const FETCH_USER_FULFILLED2 = 'FETCH_USER_FULFILLED2';
const FETCH_USER_REJECTED = 'FETCH_USER_REJECTED';
const FETCH_USER_CANCELLED = 'FETCH_USER_CANCELLED';
const fetchUser = id => ({ type: FETCH_USER, payload: id });
const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload });
const fetchUser2 = id => ({ type: FETCH_USER2, payload: id });
const fetchUserFulfilled2 = payload => ({ type: FETCH_USER_FULFILLED2, payload });
const cancelFetchUser = () => ({ type: FETCH_USER_CANCELLED });
let isFetchced = false;
const fakeAjax = url =>
Observable.of({
id: url.substring(url.lastIndexOf('/') + 1),
firstName: 'Bilbo',
lastName: 'Baggins'
}).delay(1000);
const fakeAjax2 = url =>
Observable.of({
id: url.substring(url.lastIndexOf('/2') + 1),
firstName: 'Bilbo2',
lastName: 'Baggins2'
}).delay(1000);
const fetchUserEpic = (action$, store) =>
action$.ofType(FETCH_USER)
.mergeMap(action => {
const observable = isFetchced ? Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}) : fakeAjax(`/api/users/${action.payload}`);
isFetchced = true;
console.log(action);
return observable
.map(response => fetchUserFulfilled(response))
.takeUntil(action$.ofType(FETCH_USER_CANCELLED))
});
const fetchUserEpic2 = action$ =>
action$.ofType(FETCH_USER2)
.switchMap(() => action$.ofType(FETCH_USER_FULFILLED)
.take(1)
.mergeMap(() => {
console.log("First epic");
return fakeAjax2(`/api/users/`)
.map(response => fetchUserFulfilled2(response))
}).startWith(fetchUser('redux-observable')));
const users = (state = {}, action) => {
switch (action.type) {
case FETCH_USER_FULFILLED:
return {
...state,
[action.payload.id]: action.payload
};
default:
return state;
}
};
const isFetchingUser = (state = false, action) => {
switch (action.type) {
case FETCH_USER:
return true;
case FETCH_USER_FULFILLED:
case FETCH_USER_CANCELLED:
return false;
default:
return state;
}
};
这是仿真https://jsbin.com/qitutixuqu/1/edit?html,css,js,console,output。单击控制台中的按钮 "Fetch user info" 后,您可以看到 "First epic",第二次单击该按钮后,控制台中没有任何消息。如果您将延迟添加到
Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}).delay(10)
它按预期开始工作。
简短回答:第一次点击是异步的,在 fetchUserEpic
中返回 1000 毫秒的延迟。第二次单击是 fetchUserEpic
的完全同步执行,导致内部 actions$.ofType(FETCH_USER_FULFILLED)
缺少 fetchUserEpic2
中的操作。
解释:
在第一次点击时跟踪 fetchUserEpic,我们得到这个:
fetchUserEpic src: FETCH_USER2
fetchUserEpic2 src: FETCH_USER2
fetchUserEpic2 in: FETCH_USER2
fetchUserEpic2 out: FETCH_USER
fetchUserEpic src: FETCH_USER
fetchUserEpic in: FETCH_USER
fetchUserEpic2 src: FETCH_USER <- Notice location
fetchUserEpic out: FETCH_USER_FULFILLED
fetchUserEpic src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER_FULFILLED
fetchUserEpic2-inner src: FETCH_USER_FULFILLED <- now subscribed
fetchUserEpic2-inner in: FETCH_USER_FULFILLED
First epic
fetchUserEpic2 out: FETCH_USER_FULFILLED2
fetchUserEpic src: FETCH_USER_FULFILLED2
fetchUserEpic2 src: FETCH_USER_FULFILLED2
第二次追踪得到:
fetchUserEpic src: FETCH_USER2
fetchUserEpic2 src: FETCH_USER2
fetchUserEpic2 in: FETCH_USER2
fetchUserEpic2 out: FETCH_USER
fetchUserEpic src: FETCH_USER
fetchUserEpic in: FETCH_USER
fetchUserEpic out: FETCH_USER_FULFILLED
fetchUserEpic src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER <- Notice location
由于 fetchUserEpic2
在 switchMap 语句中订阅了 actions$
,因此它不会接收已经调度的操作。 redux-observable 使用常规 Subject
,而不是 ReplaySubject
或类似的,因此如果在订阅之前分派操作,则 actions$ 订阅将错过操作。出于这个原因,当您依赖于 fetchUserEpic2
正在使用的内部订阅时,您需要小心确保异步调度操作。
这是带有跟踪日志语句的修改源:
const fetchUserEpic = (action$, store) =>
action$
.do(a => console.log(`fetchUserEpic src: ${a.type}`))
.ofType(FETCH_USER)
.do(a => console.log(`fetchUserEpic in: ${a.type}`))
.mergeMap(action => {
const observable = isFetchced ? Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}) : fakeAjax(`/api/users/${action.payload}`);
return observable
.map(response => (isFetchced = true,fetchUserFulfilled(response)))
.takeUntil(action$.ofType(FETCH_USER_CANCELLED))
})
.do(a => console.log(`fetchUserEpic out: ${a.type}`));
const fetchUserEpic2 = action$ =>
action$
.do(a => console.log(`fetchUserEpic2 src: ${a.type}`))
.ofType(FETCH_USER2)
.do(a => console.log(`fetchUserEpic2 in: ${a.type}`))
.switchMap(() =>
action$
.do(a => console.log(`fetchUserEpic2-inner src: ${a.type}`))
.ofType(FETCH_USER_FULFILLED)
.do(a => console.log(`fetchUserEpic2-inner in: ${a.type}`))
.take(1)
.do(() => console.log("First epic"))
.mergeMap(() =>
fakeAjax2(`/api/users/`)
.map(response => fetchUserFulfilled2(response))
).startWith(fetchUser('redux-observable')))
.do(a => console.log(`fetchUserEpic2 out: ${a.type}`));
我对 redux-observables 有疑问。在我的情况下,一部史诗等待另一部史诗的结束。第二个史诗可以从缓存中发出请求或 return 数据。 当第二个请求按预期工作时,但是当它 return 缓存第一个时,第一个请求不会继续。
const { Observable } = Rx;
const FETCH_USER = 'FETCH_USER';
const FETCH_USER_FULFILLED = 'FETCH_USER_FULFILLED';
const FETCH_USER2 = 'FETCH_USER2';
const FETCH_USER_FULFILLED2 = 'FETCH_USER_FULFILLED2';
const FETCH_USER_REJECTED = 'FETCH_USER_REJECTED';
const FETCH_USER_CANCELLED = 'FETCH_USER_CANCELLED';
const fetchUser = id => ({ type: FETCH_USER, payload: id });
const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload });
const fetchUser2 = id => ({ type: FETCH_USER2, payload: id });
const fetchUserFulfilled2 = payload => ({ type: FETCH_USER_FULFILLED2, payload });
const cancelFetchUser = () => ({ type: FETCH_USER_CANCELLED });
let isFetchced = false;
const fakeAjax = url =>
Observable.of({
id: url.substring(url.lastIndexOf('/') + 1),
firstName: 'Bilbo',
lastName: 'Baggins'
}).delay(1000);
const fakeAjax2 = url =>
Observable.of({
id: url.substring(url.lastIndexOf('/2') + 1),
firstName: 'Bilbo2',
lastName: 'Baggins2'
}).delay(1000);
const fetchUserEpic = (action$, store) =>
action$.ofType(FETCH_USER)
.mergeMap(action => {
const observable = isFetchced ? Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}) : fakeAjax(`/api/users/${action.payload}`);
isFetchced = true;
console.log(action);
return observable
.map(response => fetchUserFulfilled(response))
.takeUntil(action$.ofType(FETCH_USER_CANCELLED))
});
const fetchUserEpic2 = action$ =>
action$.ofType(FETCH_USER2)
.switchMap(() => action$.ofType(FETCH_USER_FULFILLED)
.take(1)
.mergeMap(() => {
console.log("First epic");
return fakeAjax2(`/api/users/`)
.map(response => fetchUserFulfilled2(response))
}).startWith(fetchUser('redux-observable')));
const users = (state = {}, action) => {
switch (action.type) {
case FETCH_USER_FULFILLED:
return {
...state,
[action.payload.id]: action.payload
};
default:
return state;
}
};
const isFetchingUser = (state = false, action) => {
switch (action.type) {
case FETCH_USER:
return true;
case FETCH_USER_FULFILLED:
case FETCH_USER_CANCELLED:
return false;
default:
return state;
}
};
这是仿真https://jsbin.com/qitutixuqu/1/edit?html,css,js,console,output。单击控制台中的按钮 "Fetch user info" 后,您可以看到 "First epic",第二次单击该按钮后,控制台中没有任何消息。如果您将延迟添加到
Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}).delay(10)
它按预期开始工作。
简短回答:第一次点击是异步的,在 fetchUserEpic
中返回 1000 毫秒的延迟。第二次单击是 fetchUserEpic
的完全同步执行,导致内部 actions$.ofType(FETCH_USER_FULFILLED)
缺少 fetchUserEpic2
中的操作。
解释:
在第一次点击时跟踪 fetchUserEpic,我们得到这个:
fetchUserEpic src: FETCH_USER2
fetchUserEpic2 src: FETCH_USER2
fetchUserEpic2 in: FETCH_USER2
fetchUserEpic2 out: FETCH_USER
fetchUserEpic src: FETCH_USER
fetchUserEpic in: FETCH_USER
fetchUserEpic2 src: FETCH_USER <- Notice location
fetchUserEpic out: FETCH_USER_FULFILLED
fetchUserEpic src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER_FULFILLED
fetchUserEpic2-inner src: FETCH_USER_FULFILLED <- now subscribed
fetchUserEpic2-inner in: FETCH_USER_FULFILLED
First epic
fetchUserEpic2 out: FETCH_USER_FULFILLED2
fetchUserEpic src: FETCH_USER_FULFILLED2
fetchUserEpic2 src: FETCH_USER_FULFILLED2
第二次追踪得到:
fetchUserEpic src: FETCH_USER2
fetchUserEpic2 src: FETCH_USER2
fetchUserEpic2 in: FETCH_USER2
fetchUserEpic2 out: FETCH_USER
fetchUserEpic src: FETCH_USER
fetchUserEpic in: FETCH_USER
fetchUserEpic out: FETCH_USER_FULFILLED
fetchUserEpic src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER_FULFILLED
fetchUserEpic2 src: FETCH_USER <- Notice location
由于 fetchUserEpic2
在 switchMap 语句中订阅了 actions$
,因此它不会接收已经调度的操作。 redux-observable 使用常规 Subject
,而不是 ReplaySubject
或类似的,因此如果在订阅之前分派操作,则 actions$ 订阅将错过操作。出于这个原因,当您依赖于 fetchUserEpic2
正在使用的内部订阅时,您需要小心确保异步调度操作。
这是带有跟踪日志语句的修改源:
const fetchUserEpic = (action$, store) =>
action$
.do(a => console.log(`fetchUserEpic src: ${a.type}`))
.ofType(FETCH_USER)
.do(a => console.log(`fetchUserEpic in: ${a.type}`))
.mergeMap(action => {
const observable = isFetchced ? Observable.of({
id: 2,
firstName: 'Bilbo',
lastName: 'Baggins'
}) : fakeAjax(`/api/users/${action.payload}`);
return observable
.map(response => (isFetchced = true,fetchUserFulfilled(response)))
.takeUntil(action$.ofType(FETCH_USER_CANCELLED))
})
.do(a => console.log(`fetchUserEpic out: ${a.type}`));
const fetchUserEpic2 = action$ =>
action$
.do(a => console.log(`fetchUserEpic2 src: ${a.type}`))
.ofType(FETCH_USER2)
.do(a => console.log(`fetchUserEpic2 in: ${a.type}`))
.switchMap(() =>
action$
.do(a => console.log(`fetchUserEpic2-inner src: ${a.type}`))
.ofType(FETCH_USER_FULFILLED)
.do(a => console.log(`fetchUserEpic2-inner in: ${a.type}`))
.take(1)
.do(() => console.log("First epic"))
.mergeMap(() =>
fakeAjax2(`/api/users/`)
.map(response => fetchUserFulfilled2(response))
).startWith(fetchUser('redux-observable')))
.do(a => console.log(`fetchUserEpic2 out: ${a.type}`));