Sunday, 3 June 2018

Managing long running task in React Native


In React Native network request is normally done with non-blocking method like fetch(). On receiving result redux-thunk middleware can be used to dispatch the new state.

Now there is a question: how to make our own non-blocking method for performing lengthy computation. This can be done using 3rd-party native extensions that introduce threading or WebWorkers to react native, or writing our own extension. There is also a workaround with creating a WebView and moving the long running function there.

There is a lighter solution, that would block the JS thread, but it allows for showing the progress indicator. Assume I want to perform an action that takes a few seconds, but first shows a progress indicator.

This action would block JS thread:
export const getData = () => {
   for (i = 0; i < 5000; i++) {
       console.log(i)
   }
   return { type: DATA_READY } 
}


To show the progress let us switch from 'return' to redux-thunk 'dispatch', and add additional state change before the computation. The flag 'loading' would be used to show progress bar in view.
export const getData = () => {
  return (dispatch) => {
    dispatch({type: DATA_CALCULATION loading: true})

    for (i = 0; i < 5000; i++) {
       console.log(i)
    }

    dispatch( { type: DATA_READY loading: false })
  }
}


The progress would not be shown despite the new state would have 'loading:true' set before computation. This happens because getData() action blocks for quite some time after the first dispatch(). This prevent the view from receiving its render() call. To make the view render before the blocking code we should move the lengthy cycle into the requestAnimationFrame() callback:
export const getData = () => {
  return (dispatch) => {
    dispatch({type: DATA_CALCULATION loading: true})

    requestAnimationFrame(() => {

       for (i = 0; i < 5000; i++) {
          console.log(i)
       }

       dispatch( { type: DATA_READY loading: false })
   })
  }
}


The requestAnimationFrame() callback is invoked before next repaint, and view now gets its render() call before the 'for' loop being launched. Progress indicator is shown and then it gets updated on main (UI) thread, while JS thread would be blocked.

No comments :

Post a Comment