4

I'm working on an app which has to manage a large amount of data. In the init process several api calls must be done while the user sees a loading bar.

Here is my init action:

export function init(key) {

  return (dispatch, getState) => {

    // start init
    dispatch(initStart());

    setTimeout(() => {
      dispatch(initProcess(10));
    }, 0)


    return Promise.all([

      // load users
      dispatch(loadUsers(key)).then(() => {
        dispatch(initProcess(30));
      }),

      // load other stuff
      // ...

      // load articles
      dispatch(loadArticles(key)).then(() => {
        dispatch(initProcess(60));
      }),

    // should be executed after all actions/reducers are done
    ]).then(() => {
      setTimeout(() => {
        dispatch(initFinish());
      }, 700);
    });
  }
}

So far it works perfectly, but there will be 20k to 50k articles. The backend has to perform some joins to get the data together, so I'm sure I'll get a server timeout if I try to get them in one piece.

The idea is to fetch the total number first and then get the articles in 1k pieces in a loop. But it wont work the way I need it. I'm getting initFinish dispatched after the articles are counted but not after they are fetched.

Here is the loadArticles action:

export function loadArticles(key) {
  return (dispatch, getState) => {

    // check local db first

    // get total number
    return dispatch(countArticles(key))
    .then(result => {
      Promise.all([
        // No idea how to put the loop in here :(
        dispatch(fetchArticles(key, 1000)),
      ])
    });
 }

}

I have no loop yet but thats not the point. The logic remains the same. I return the dispatch(countArticles(key)) before fetchArticles is done.

Has anyone a hint? That would be awesome.


EDIT

coutArticles and fetchArticles

function countArticles(key) {
  return {
    [CALL_API]: {
      types: [ COUNT_ARTICLES_REQUEST, COUNT_ARTICLES_SUCCESS, COUNT_ARTICLES_FAILURE ],
      endpoint: `articles`,
      schema: Schemas.ARTICLE_COUNTER
    }
  }
}
function fetchArticles(key, take, skip) {
  return {
    [CALL_API]: {
      types: [ FETCH_ARTICLES_REQUEST, FETCH_ARTICLES_SUCCESS, FETCH_ARTICLES_FAILURE ],
      endpoint: `articles/${take}/${skip}`,
      schema: Schemas.ARTICLE_ARRAY
    }
  }
}

The middleware is the same es here


2. EDIT

if i change

// get total number
return dispatch(countArticles(key))
.then(result => {
  Promise.all([
    // No idea how to put the loop in here :(
    dispatch(fetchArticles(key, 1000)),
  ])
});

to

// get total number
dispatch(countArticles(key))
.then(result => {
  return Promise.all([
    // No idea how to put the loop in here :(
    dispatch(fetchArticles(key, 1000)),
  ])
});

I get Uncaught TypeError: Cannot read property 'then' of undefined on dispatch(loadArticles(key)).


3. EDIT

Some days later I'm still fighting ^^

Here is the simplified init function, which should (only) get the count result and then fetch the articles:

But for now im failing already here:

export function init(key) {

  return (dispatch, getState) => {

    countArticles(key).then(result => {
      console.log(result);
    });

  }
}

Output:

Uncaught TypeError: countArticles(...).then is not a function
Gordon Freeman
  • 3,260
  • 1
  • 22
  • 42

1 Answers1

1

I had problems with chaining dispatch as well; it should return a Promise but I could not get it to work.

So I would change the logic this way

countArticles(key).then(result => {
    dispatch( {
        type:  RECEIVED_NUM_ARTICLES,
        value: result
    })
    Promise.all([...Array(Math.floor(result/1000))].map(_ => 
         fetchArticles(key,1000).then(res => dispatch( {
            type:  RECEIVED_1000_ARTICLES,
            value: res
         })
    )).then( _ => dispatch({
            type:  RECEIVED_EVERYTHING
            value: 'whatever'
         })
    )
)

(I did not test this code)

But basically: fetch then dispatch the result, then chain another fetch/dispatch sequence, etc...

The loop needs rework to fetch the modulo

The advantage with this approach is that you can update the UI with the number of articles when you have them, then provide updates on every 1000 articles fetched and finally notify when done

Bruno Grieder
  • 28,128
  • 8
  • 69
  • 101
  • I know, you've said its not tested, but should it be something like `return dispatch(countArticles(key)).then ... ` because I'm getting an error `countArticles(...).then is not a function` – Gordon Freeman Feb 17 '16 at 18:18
  • Oh, I think you focused on the loop, but that is not my main problem. In fact it's currently not a problem at all. I'm having trouble with the order of the dispatched actions. – Gordon Freeman Feb 17 '16 at 18:29
  • Isn't `countArticles` returning a `Promise` following an Ajax call ? – Bruno Grieder Feb 17 '16 at 18:38
  • Anyway, the las comment stands: fetch -> dispatch -> fetch -> dispatch; do not chain dispatches, chain fetches – Bruno Grieder Feb 17 '16 at 18:45
  • You should not try to return the `countArticles` dispatch. It causes a race condition with `fetchArticles()`. You will need to wrap it in the `dispatch` function, however, because the middleware returns a `Promise`. Use the code above and just modify it to read `dispatch(countArticles(key)).then(...)` and it should work. – Mike Feb 17 '16 at 19:00
  • thank you both. I'll try. I bet it is not that difficult, but its new for me ;) – Gordon Freeman Feb 17 '16 at 19:06
  • hey guys, i've let it sink for some days, but now I'm not really further. Would you please check the 3rd edit? – Gordon Freeman Feb 24 '16 at 19:39