admin管理员组

文章数量:1355694

I have a Saga where I need to do 3 asynchronous requests, then use the responses from the 3 requests in a follow-up request. Here's some psuedo-code to explain:

function* useOtherActionsAndSagas(action) {
  try {
    const [response1, response2, response3] = yield [
      request1,
      request2, 
      request3
    ];

    const orderData = {
      ...response1,
      ...response2,
      ...response3,
    };

    const response4 = yield request4;

    yield put({ type: 'SUCCESS', data: response4 });
  } catch (e) {
    // handle error
  }

The 3 requests request1, request2 and request3 correspond to 3 separate Sagas. For example, for request1 there's a Saga along the lines of:

export function* request1(action) {
  try {
    const result = yield api.get(`/api/endpoint`);
    yield put({...action, type: ACTION1_SUCCESS, data: result});
  } catch (e) {
    yield put({...action, type: ACTION1_FAIL, errors: e});
  }
}

function* watchAction1() {
  yield* takeLatest(ACTION1, request1);
}

export default function* () {
  yield [fork(watchAction1)];
}

where api.get is a wrapper for Axios.get().

This watcher in that Saga is connected to a corresponding action/reducer.

export const ACTION1 = "actions/ACTION1";
export const ACTION1_SUCCESS = "actions/ACTION1_SUCCESS";
export const ACTION1_FAIL = "actions/ACTION1_FAIL";

const initialState = {
  // Initial state
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case ACTION1:
      // return state
    case ACTION1_SUCCESS:
      // return state
    case ACTION1_FAIL:
      // return state
    };
    default:
      // return state;
  }
}

export function request1(data) { return {type: ACTION1, data}; }

To keep my code DRY I was hoping to take advantage of the existing action and saga in the parent saga. To do this I tried:

const [response1, response2, response3] = yield [
  put({type: ACTION1, data: data1}),
  put({type: ACTION2, data: data2}),
  put({type: ACTION3, data: data3})
];

This correctly initiates each action and their corresponding sagas. However, the response from the requests are not available in the assigned variables. That is, response1, response2 and response3 are references to their actions {type: "actions/ACTION1", data: data1} and not a Promise.

I know it would be possible to duplicate the Axios requests in this parent Saga but I'd lose the bonus of having the success/fail actions for the individual requests.

Is it possible to use a setup like this? If so, how can the responses from the asynchronous requests be retrieved for use in a follow-up request?

If not, what is the correct method for acplishing this?

Update

I can use the workers from the other Sagas within the parent saga, like this:

import request1 from request1Saga;

const [response1, response2, response3] = yield [
  call(request1, data1),
  call(request2, data2),
  call(request3, data3),
];

where request1, request2 and request3 are the worker functions from other Sagas. That gives the benefit of the ACTION1_SUCCESS and ACTION1_FAIL actions from those Sagas being used.

I have a Saga where I need to do 3 asynchronous requests, then use the responses from the 3 requests in a follow-up request. Here's some psuedo-code to explain:

function* useOtherActionsAndSagas(action) {
  try {
    const [response1, response2, response3] = yield [
      request1,
      request2, 
      request3
    ];

    const orderData = {
      ...response1,
      ...response2,
      ...response3,
    };

    const response4 = yield request4;

    yield put({ type: 'SUCCESS', data: response4 });
  } catch (e) {
    // handle error
  }

The 3 requests request1, request2 and request3 correspond to 3 separate Sagas. For example, for request1 there's a Saga along the lines of:

export function* request1(action) {
  try {
    const result = yield api.get(`/api/endpoint`);
    yield put({...action, type: ACTION1_SUCCESS, data: result});
  } catch (e) {
    yield put({...action, type: ACTION1_FAIL, errors: e});
  }
}

function* watchAction1() {
  yield* takeLatest(ACTION1, request1);
}

export default function* () {
  yield [fork(watchAction1)];
}

where api.get is a wrapper for Axios.get().

This watcher in that Saga is connected to a corresponding action/reducer.

export const ACTION1 = "actions/ACTION1";
export const ACTION1_SUCCESS = "actions/ACTION1_SUCCESS";
export const ACTION1_FAIL = "actions/ACTION1_FAIL";

const initialState = {
  // Initial state
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case ACTION1:
      // return state
    case ACTION1_SUCCESS:
      // return state
    case ACTION1_FAIL:
      // return state
    };
    default:
      // return state;
  }
}

export function request1(data) { return {type: ACTION1, data}; }

To keep my code DRY I was hoping to take advantage of the existing action and saga in the parent saga. To do this I tried:

const [response1, response2, response3] = yield [
  put({type: ACTION1, data: data1}),
  put({type: ACTION2, data: data2}),
  put({type: ACTION3, data: data3})
];

This correctly initiates each action and their corresponding sagas. However, the response from the requests are not available in the assigned variables. That is, response1, response2 and response3 are references to their actions {type: "actions/ACTION1", data: data1} and not a Promise.

I know it would be possible to duplicate the Axios requests in this parent Saga but I'd lose the bonus of having the success/fail actions for the individual requests.

Is it possible to use a setup like this? If so, how can the responses from the asynchronous requests be retrieved for use in a follow-up request?

If not, what is the correct method for acplishing this?

Update

I can use the workers from the other Sagas within the parent saga, like this:

import request1 from request1Saga;

const [response1, response2, response3] = yield [
  call(request1, data1),
  call(request2, data2),
  call(request3, data3),
];

where request1, request2 and request3 are the worker functions from other Sagas. That gives the benefit of the ACTION1_SUCCESS and ACTION1_FAIL actions from those Sagas being used.

Share Improve this question edited Jun 12, 2017 at 13:26 Brett DeWoody asked Jun 12, 2017 at 11:37 Brett DeWoodyBrett DeWoody 63k31 gold badges144 silver badges192 bronze badges 8
  • Which version of redux-saga are you using? I think from v0.15.0 they use yield all([]) now Have a look at - github./redux-saga/redux-saga/blob/… – Sudheer Commented Jun 12, 2017 at 12:00
  • Currently on 0.12.1, I can update but I don't think that will fix the issue of the responses. – Brett DeWoody Commented Jun 12, 2017 at 12:06
  • Ya I dont think it will make any difference. One more thing though isn't yield [] looking for promise object? here you have just state return though. – Sudheer Commented Jun 12, 2017 at 12:10
  • Yes, that's the crux of the issue I think. I want to reuse the external action/saga to take advantage of their success/fail actions, as well as not duplicate the existing request already in another saga. – Brett DeWoody Commented Jun 12, 2017 at 12:17
  • 1 I suppose that will work if I passed the Saga worker from the sub-sagas to each fork(). I'm doing something similar with the array of requests now. – Brett DeWoody Commented Jun 12, 2017 at 12:54
 |  Show 3 more ments

1 Answer 1

Reset to default 3

All you need is bine all binator with call effect (docs for posing sagas and running tasks in parallel):

const [response1, response2, response3] = yield all([
  call(request1),
  call(request2), 
  call(request3)
]);

This will execute sagas in parallel and return results from each of them. It works as Promise.all.

The sagas above (request1 to request3) need to return some data at the end of saga:

export function* request1(action) {
  try {
    const result = yield call(url => api.get(url), `/api/endpoint`);
    yield put({...action, type: ACTION1_SUCCESS, data: result});
    // This will be assigned to result1
    return result
  } catch (e) {
    yield put({...action, type: ACTION1_FAIL, errors: e});
  }
}

Note: You don't need to fork takeEvery, because it is already "forked":

// Example of root saga:
export default function* () {
  yield takeLatest(ACTION1, request1);
  yield takeLatest(ACTION2, request2);
  // ...
}

本文标签: javascriptUsing ActionSaga in another SagaStack Overflow