React-native: How to run heavy JS blocking tasks which won't affect the main UI thread?

Created on 26 May 2018  路  6Comments  路  Source: facebook/react-native

Hello! I'm working on chess app which makes a lot of simulations for analytics and AI play mode. This freezes the UI and main thread for 2-3 seconds because of all processes running.
I had BIG research on how to solve that.

Firstly I want to mention that async functions and Promises do not help because they're running in the main thread and it still freezes. And also libraries like hamsters.js cannot help.

Also I found some RN libs and plugins for workers: https://github.com/fabriciovergal/react-native-workers, https://github.com/devfd/react-native-workers, which are no longer maintained and don't work with newer versions. There is one more library: https://github.com/joltup/react-native-threads. It also fails:

Also there's requestAnimation API wich cannot help in my case, because the whole processing is provided by library called RSG Chess API so I run:

AI(something)

instead of:

for (something) {
  something
}

so I cannot break the computing into smaller processes.

Another possible solution is to use "invisible" WebView in the app and there run the processes and comunicating via the window.postMessage API. Link to blog post: https://medium.com/@inkdrop/a-simple-way-to-run-js-in-background-thread-on-react-native-8fff345576da. But that is very tricky and hacky.

I had big research about the problem

Stack Overflow

Discussion Forum - https://discuss.reactjs.org/t/which-is-the-best-way-to-run-heavy-tasks-in-react-native/11932

Another sources:

Reactiflux Chat - didn't get any help.

Please help!

Ran Commands Locked

Most helpful comment

@vikas5914 Yup... As I described before there are already a lot of posts and articles about this topic. However they're incomplete and little bit tricky.

Every step is listed below. Before to read them please join my slackroom http://rsg-slack.herokuapp.com and I'll help you achieve multithreading. Also this solution is implemented in the RSG-Chess-mobile repository. Take a look here:

  1. Read this medium post: https://medium.com/@inkdrop/a-simple-way-to-run-js-in-background-thread-on-react-native-8fff345576da
  2. You'll need to make html file or provide it as string.
    Example:
// in your js code
const html = `
<html>
  <body>
    <script>
      // The code which will be run on the web thread.
    </script>
  </body>
</html>`;

If you want to use any node/ES6 modules or npm packages you'll need to make a new file and then compile it with webpack and babel or use online tool.
In my case I needed the RSG Chess API module and copy-pasted its code from the official repo, like that:
image

  1. Add WebView to your app.
// example from the Medium post, but using your custom html and scripts
<WebView
     ref={el => this.webView = el}
     source={{html: html}}
     onMessage={this.handleMessage}
    />

the html const must be a string. You can also create html file and use the nodejs apis to pass it to the WebView.

  1. Initialize global functions to pass arguments/data to it.
<script>
    window.AI = function (params) {
      // code here
      // return some response to the main RN JS thread
      window.postMessage();
    }
</script>
  1. Call the global functions from the main thread.
    To run the methods from the main RN JS thread you can simple use the injectJavaScript method. In my case I used:
this.webView.injectJavaScript(
   `AI(${params})`
)

this.webView is defined by ref={el => this.webView = el}

In the example above params is stringified object, because you must send and recieve only STRINGS via the postMessageAPI and the injectJavaScript method. Don't worry you can pass objects by using JSON.stringify and JSON.parse

  1. Send back the results
    When your tasks in the web thread are ready you can send back response to the main thread
window.postMessage(/* string, stringified object/array or stringified function */);

To recieve the data you can use handleMessage method predefined in the onMessage prop of the WebView.

<WebView onMessage={this.handleMessage} />
handleMessage = msg => {
  msg.nativeEvent.data
}

Hopefully this will help!

All 6 comments


It looks like your issue may be incomplete. Are all the fields required by the Issue Template filled out?

If you believe your issue contains all the relevant information, let us know in order to have a maintainer remove the No Template label.

If you are still encountering the issue described here, please open a new issue and make sure to fill out the Issue Template when doing so.

@radi-cho Can you tell me how did you fix the problem.

@vikas5914 Yup... As I described before there are already a lot of posts and articles about this topic. However they're incomplete and little bit tricky.

Every step is listed below. Before to read them please join my slackroom http://rsg-slack.herokuapp.com and I'll help you achieve multithreading. Also this solution is implemented in the RSG-Chess-mobile repository. Take a look here:

  1. Read this medium post: https://medium.com/@inkdrop/a-simple-way-to-run-js-in-background-thread-on-react-native-8fff345576da
  2. You'll need to make html file or provide it as string.
    Example:
// in your js code
const html = `
<html>
  <body>
    <script>
      // The code which will be run on the web thread.
    </script>
  </body>
</html>`;

If you want to use any node/ES6 modules or npm packages you'll need to make a new file and then compile it with webpack and babel or use online tool.
In my case I needed the RSG Chess API module and copy-pasted its code from the official repo, like that:
image

  1. Add WebView to your app.
// example from the Medium post, but using your custom html and scripts
<WebView
     ref={el => this.webView = el}
     source={{html: html}}
     onMessage={this.handleMessage}
    />

the html const must be a string. You can also create html file and use the nodejs apis to pass it to the WebView.

  1. Initialize global functions to pass arguments/data to it.
<script>
    window.AI = function (params) {
      // code here
      // return some response to the main RN JS thread
      window.postMessage();
    }
</script>
  1. Call the global functions from the main thread.
    To run the methods from the main RN JS thread you can simple use the injectJavaScript method. In my case I used:
this.webView.injectJavaScript(
   `AI(${params})`
)

this.webView is defined by ref={el => this.webView = el}

In the example above params is stringified object, because you must send and recieve only STRINGS via the postMessageAPI and the injectJavaScript method. Don't worry you can pass objects by using JSON.stringify and JSON.parse

  1. Send back the results
    When your tasks in the web thread are ready you can send back response to the main thread
window.postMessage(/* string, stringified object/array or stringified function */);

To recieve the data you can use handleMessage method predefined in the onMessage prop of the WebView.

<WebView onMessage={this.handleMessage} />
handleMessage = msg => {
  msg.nativeEvent.data
}

Hopefully this will help!

thanks to @radi-cho and his project RSG Chess project, I solved the problem.
The example is:
https://pastebin.com/DG490YJw
https://pastebin.com/t6w1i9EX
I have react native 0.53.0.

So we with @invyctus92 resolved his issue. There were two problems with his code.

  1. You need to pass source={{ html: html }} to the webView
    Wrong case: source={{ html }} which will pass { html: true } to the WebView
    Right case: source={{ html: html }} which will pass { html: CONTENT_IN_YOUR_html_VAR }

  2. He needed to run the web thread in the componentDidMount method, but sometimes the webView is not yet loaded at this time. For that purpose you can use the state or a callback to check if everything is loaded.

Working examples:

Also I tried to make this issue as clean as possible, so If someone have additional questions please contact me here http://rsg-slack.herokuapp.com But if someone have better solution or code which will help the community please post here!

Was this page helpful?
0 / 5 - 0 ratings