史诗对另一部史诗的行动没有反应

时间:2017-07-15 18:06:54

标签: redux rxjs react-redux rxjs5 redux-observable

我对redux-observables有疑问。在我的情况下,一个史诗般的等待结束另一个史诗。第二个史诗可以从缓存中发出请求或返回数据。 当第二个使请求全部按预期工作时,但当它返回缓存时,第一个请求不会继续。

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/${1}`)
            .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。点击按钮"获取用户信息"在控制台中你可以看到"第一个史诗",在第二次点击按钮后,控制台中没有消息。如果您将延迟添加到

Observable.of({
  id: 2,
  firstName: 'Bilbo',
  lastName: 'Baggins'
}).delay(10)

它按预期开始工作。

1 个答案:

答案 0 :(得分:3)

简答:第一次点击是异步的,在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或类似的,因此如果在订阅之前调度操作,那么操作$ subscription将错过该操作。因此,您需要注意保证在依赖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/${1}`)
            .map(response => fetchUserFulfilled2(response))
        ).startWith(fetchUser('redux-observable')))
    .do(a => console.log(`fetchUserEpic2 out: ${a.type}`));