How to join a collection of Promises asyncrounously in a Saga?
Asked Answered
C

2

6

Within a redux saga I am sending a half dozen fetch requests to different systems. I want to wait until all those requests return, then do some final processing on the results.

To this end I have an array of promises, representing each query. I could call Promise.all() on the array, but this would cause the saga to hang, and thus all events to hang, until the promises return.

I tried creating an async promise that called promise.all, then using the redux-effects Call on that promise, but this also hung.

How can I keep the async nature of my saga while waiting for the promises to return?

Confluence answered 15/8, 2018 at 13:17 Comment(3)
I'm not sure I'm following the question. If you need all responses back to complete the final processing, then you must wait for the responses to come back. There's no getting around that. While the saga is waiting for the responses to come back, the rest of your app will continue to receive events. The saga will hang or pause execution until all responses come back, but it won't block the js event loop. This seems obvious, so I feel like I must be missing the question.Dato
@Dato It is blocking the JS event loop for me, and that is what I would expect as behavior as well. Since doing something like calling promise.all would casue a blocking wait the saga's event won't complete (or yield). It continues executing, even though it's only executing a sleep command, preventing other event's from running. Specifically I see actions dispatched to change sorting on my table not execute until the saga completes when I use that approach.Confluence
This seems odd for sure. How are you invoking the saga?Dato
D
14

To run all request in parallel you should use the all effect from redux-saga. Its analogous to the Promise.all method you referenced already.

Example:

import { fetchCustomers, fetchProducts } from './path/to/api'
import { all, call } from `redux-saga/effects`

function* mySaga() {
  const { customers, products } = yield all({
    customers: call(fetchCustomers),
    products: call(fetchProducts)
  });
  // do something with results
}

This is the most straight forward way to run asynchronous operations in parallel and wait for all processes to complete. This approach will not block the javascript event loop. It will only block the remainder of the generator function from running. Other actions in other sagas, and other events (such as clicks), will still be received by your application while the requests are in flight.

Please refer to the official docs for more information.

Dato answered 15/8, 2018 at 16:50 Comment(0)
M
0

You can do something like this

*getProductsSaga() {
    while (true) {
      yield take(types.GET_PRODUCTS_REQUEST);
      try {
        const result1 = yield call(() => getProducts1Promise());
        const result2 = yield call(() => getProducts2Promise());
        const result3 = yield call(() => getProducts3Promise());
        const result4 = yield call(() => getProducts4Promise());
        yield put({
          type: types.GET_PRODUCTS_SUCCESS,
          payload: [result1, result2, result3, result4] // process/combine results depending on how you want
        });
      } catch (error) {
        yield put({
          type: types.GET_PRODUCTS_FAILURE,
          payload: error
        });
      }
    }
  }
Musette answered 15/8, 2018 at 13:25 Comment(1)
If you do this aren't you effectively doing them in order. getProducts2Promise is not called until getProducts1Promise completes fully. The whole point is to make all of the calls out at once so I can get quicker responses then doing calls in order.Confluence

© 2022 - 2024 — McMap. All rights reserved.