Skip to content

Move JS Processing to background thread or allow creation of other WebWorkers/Threads #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
NathanaelA opened this issue Apr 27, 2015 · 92 comments

Comments

@NathanaelA
Copy link
Contributor

From what I understand everything runs on the primary thread. So this is a feature request -- but if done possible sooner would eliminate any later incompatibilities. Having the main thread run the interface and JS runtimes means that hiccups can and will occur frequently. Either the design where the interface is run on its own thread; and the engine is on its own or the ability to start webworks will eliminate these types of issues where you need to do any long running calculations.

Please vote for this feature in our ideas portal.

@RangerMauve
Copy link

Yeah, I'm pretty sure ReactNative does something along those lines at the moment. Even if JS is in its own thread, I still think that having something like a WebWorker would be very useful feature to have.

@emmanuelbuah
Copy link

+1 I made a comment about this on the Q&A blog post and didn't get a respond. I think this is important to, especially when animation or long background task comes into play. I hope other upvotes this feature. It very important for performance.

@wan54
Copy link

wan54 commented May 19, 2015

+1

@ligaz
Copy link
Contributor

ligaz commented May 21, 2015

My reply was originally posted on the forums but I'm reposting it here is well:

This is really a tough engineering problem to solve as there is no silver bullet here. For example we can introduce WebWorkers but they do have their flaws as well. There is this new re-thinking of WebWorkers in the web space - SharedWorker that tries to solve some of the limitations there. There are other concept like immutable data structures that are slowly gaining momentum.

I also want to mention that React Native solution has its own flaws. Being always on a separate thread means that you will constantly have to marshall data (in their case serializing/deserializing JSON) between the JS thread and the UI thread. This might be beneficial for compute intensive JavaScript but in the general case the price you pay is significantly higher compared to doing it in the UI thread.

In order for us to provide the right solution we need your use cases. What is your particular scenario that you need to perform compute intensive JavaScript operations? Having more use cases on the table will help us drive this forward.

@RangerMauve
Copy link

Persoanlly, more I'm a fan of the code running in the UI thread by default. I just think that having the option to run things in a worker would be a useful feature to have for projects that would need a separate thread for heavy computations and what not. But even more so, it'd be nice if we had an abstraction that would let us write something like and Android Service which would let our JS code run even when it's not part of the UI of an app.

@ndarilek
Copy link

ndarilek commented May 21, 2015 via email

@RangerMauve
Copy link

+1 to what @ndarilek is saying. I think that ServiceWorker is a better abstraction than WebWorkers for this case, though. Since WebWorkers are more like a separate thread and must be started by the UI, where as a ServiceWorker can run even when a website isn't even opened.

@ndarilek
Copy link

ndarilek commented May 21, 2015 via email

@NathanaelA

This comment was marked as abuse.

@VinceOPS
Copy link

Hi everyone,

I looked at the documentation (perhaps I did it wrong) and saw nothing about multi-threading too. This thread is actually quite new so I was simply wondering the status of this request?

Thanks a lot. Can't wait to see all NativeScript possibilities.

Regards.

@valentinstoychev
Copy link

Hi @VinceOPS, this issues is still not in active development. We treat it with high priority and we are evaluating what will be the best strategy here based on all the feedback.

Can you please share your scenario that requires multithreading.

Thanks!

@VinceOPS
Copy link

Hi @valentinstoychev. Understood!

The typical scenario would be any common background task like fetching some data from a webservice (which could eventually timeout) or a database. Inserting group of rows in a database, too. Generating a .CSV file (sent, later, by email) with few hundred or thousands or entries...

I hope it helps.

Thanks!

Regards.

@valentinstoychev
Copy link

Thanks @VinceOPS - this helps a lot!

@NekR
Copy link

NekR commented Jul 23, 2015

@ligaz

This might be beneficial for compute intensive JavaScript but in the general case the price you pay is significantly higher compared to doing it in the UI thread.

Thing here is that React's JS computations is heavy, this is why they went native and on separate thread by default. Here, for {N} is not real need to be by default on separate thread, but having ability to run system thread with JS handling would be good since without it smooth UI is hard thing (see how hard it's for mobile web to live on one thread).

@cime
Copy link

cime commented Aug 26, 2015

Anything new on this one?
Image/Bitmap processing is one more thing that could be done in background.

@hshristov
Copy link
Contributor

@cime Bitmap decoding is already on a background thread if you are using our http/fetch module to download a picture. For 1.4.0 we will try to move all image decoding operations in the background no matter where the picture is loaded from.

@cime
Copy link

cime commented Aug 26, 2015

Sorry, I wasn't clear. I mean custom bitmap processing like rotations etc.
Ideally I would like to have something like this:

var backgroundTasks = require("background-tasks");
backgroundTasks.doInBackground(function() {
    // my JS code here
    // return data;
}).then(function(data){
    // data = result that you return from callback above
}).catch(function(exception) {
    // exception = native exception or exception that you throw in callback
});

@meticoeus
Copy link

A few use cases I need to cover and would ideally (or necessarily) handle off the UI thread.

Web socket communication with a server to update a local cache of the database the user needs access to, which would be updated in diffs (on child added/changed/removed events), then scheduling cleanup operations to trim or update the oldest accessed items in the local cache. The UI thread would only interact the local cache in this scenario.

Custom image/file processing, such as cropping a photo before uploading, or reading image EXIF data.

Background service to sip GPS data over time while the app is not open and send that data to the server, potentially executing callbacks based on proximity to geo-fencing data.

@stephenfeather
Copy link
Contributor

Think the others have hit most of my pain points ready, so I'll add a +1 for image processing/manipulation as a function that can be intensive and tends to be a bit of a blocker.

+1 Database processing.
+1 gps/Beacon radio access
+1 network access
+1 xml/json parsing

All have aspects that get intense and cause blocking

@TheBrousse
Copy link

To me, network and database access would be the items that are the most likely to have an impact on the UI thread (thus causing hiccups when doing intensive processes)

@RangerMauve
Copy link

Could one hypothetically just use the Java Thread class? Or would that cause issues since v8 runs javascript single threaded?

@PeterStaev
Copy link
Contributor

👍
@RangerMauve sadly the java Thread class is a no go. It still executes the run method on the main thread. Sames goes for android.os.AsyncTask

@vchimev vchimev added this to the 1.5 (Under Review) milestone Oct 6, 2015
@maknz
Copy link

maknz commented Oct 11, 2015

👍 on this. Even though the response to network calls is async, the work involved in issuing the requests is enough to make our UI too slow to use, particularly on Android, where doing anything in an onNavigatedTo callback blocks the navigation, which isn't a problem on iOS so much.

@vchimev vchimev removed this from the 1.5 milestone Oct 19, 2015
@ndarilek
Copy link

ndarilek commented Mar 23, 2016 via email

@PeterStaev
Copy link
Contributor

@atanasovg I partially agree with you. But i think if we go to force users write native code, there must be a better solution than using XCode/Android studio and making separate projects. May be have a native folder in the app structure with subfolders for android and ios where we can include native files that will be compiled on build? Otherwise for something simple (the situation I gave example above) would turn in so much overhead to manage 3 separate projects.

@slavchev
Copy link

@ndarilek

Thanks for your feedback. I don't find your comment offensive and I don't think any of the team find it either. I like your approach and I appreciate it.

Now, let's go back to the issue. Currently, we don't know how to provide a good threading model in {N}. And I want to emphasize on "good". All we know is that we won't make it right from the first try. Our team discussed internally this issue for a long time and indeed it is hard one. One of my main reasons to ask for use cases is that I have some concrete ideas how this feature can be implemented. I cannot get any meaningful information from other implementations because there no implementations. My main concerns is how modules and plugins have to work.

For example, what are your expectations when you require("application") on a non-UI thread (new JavaScript context)? What are your expectations when you use a single native object, say File, in two different threads (two different js objects that point to one shared native object)? Do you expect some presence of threading synchronization mechanisms? If that is the case, which ones? Do you want require("module") to be initialized on every thread/context? If you are a plugin author how are you going to explain to the developers how they should use your module/plugin? What about the native object ownership? Do you want a native object created on one thread/context to be accessible on another one? What model do you prefer?

These are only part of the questions our team has discussed. So far, it seems much easier to stick with some event-driven approach, similar to Node.js, which perfect for I/O scenarios and not so suitable for parallel computations. We know we are going to make some trade-offs and asking for your scenarios we collect data so we can present you some initial approach/implementation which we will then start to discuss with you guys.

@ndarilek
Copy link

Got it. I think this list of questions is more helpful because, as a
potential developer, I can think about them more precisely and give more
helpful answers. More specifically:

  • I think it's reasonable for each thread to have an entirely separate
    context, and for boundaries to cross only at specific points. I could
    see spinning up a background thread for geospatial processing, and that
    thread firing events to/accepting messages from the UI, but I think it's
    reasonable for them to not access each others' objects,
    required/imported modules, etc.
  • Likewise, I think it would be reasonable for the native objects to
    be entirely inaccessible. So a file accessed on a separate background
    thread couldn't be affected by the UI, or any other thread.
  • In terms of message-passing, maybe something that looks a bit like
    an EventEmitter? Each thread gets one, and can pass an event along with
    some basic data across bounds (Android Parcelables, for instance.) It is
    each thread's responsibility to construct that data into a way that
    makes sense. This might result in, say, a file being broken down into a
    JSON structure representing it ({"path": "/path/to/file"} for example)
    being reconstituted as an equivalent file on the other side of the
    boundary, but it also avoids a whole host of issues. I could do
    something like bridge.emit("locationChanged", {latitude, longitude, heading}) and the UI could do bridge.on("locationChanged", (err, location) => updateMapDisplay(location)) and that would be hugely helpful.

Having said all that, I'm not necessarily advocating this as the way to
go forward, and it may not work for others. But in thinking of the kinds
of problems I'd like to solve with NativeScript, React Native or
whatever non-native runtime I want on my phone, and in terms of the
issues you've raised, having a separate JS context for background tasks
and passing a limited subset of messages across thread bounds would
probably get me a long way to where I want to be.

I hope that helps. Thanks for putting it in terms of questions/specific
issues.

@PeterStaev
Copy link
Contributor

For example, what are your expectations when you require("application") on a non-UI thread (new JavaScript context)? What are your expectations when you use a single native object, say File, in two different threads (two different js objects that point to one shared native object)? Do you expect some presence of threading synchronization mechanisms? If that is the case, which ones? Do you want require("module") to be initialized on every thread/context? If you are a plugin author how are you going to explain to the developers how they should use your module/plugin? What about the native object ownership? Do you want a native object created on one thread/context to be accessible on another one? What model do you prefer?

@slavchev I think even allowing simplified threads with having access to only native classes will be a big 👍 for {N}.

  • I think access to modules should be completely disallowed. If I want to access something via module then I can rewrite it and use the native classes.
  • Plugins - if the plugin is slow and requires the end user to use threads, then the plugin is written poorly and the plugin itself should be using a thread. Similar to modules I think access to plugins should be disallowed in the thread.
  • The crossing between the spawned thread and the current one should happen only in the success calback, error calback and if possible some progress calback where we can update the UI.
  • Parameters and return result could be limited to simple types and JSON objects.

I think even with having these limitations will handle most (if not all) the situations above

@NathanaelA

This comment was marked as abuse.

@atanasovg
Copy link
Contributor

@NathanaelA So, to rephrase what you are saying - because {N} does not allow you to run JS on another thread you execute CPU intensive code on the main thread, which freezes the UI. Well, every GUI platform strongly advises against running blocking code on the main UI thread. Btw, would you share what kind of work you are actually performing in your screenshots? I'd love to check whether this is a common application scenario.

Now, let's accept for a moment that background JS processing is a currently not available feature in {N}. And the {N} team may not engage with any particular time frame for implementing it. What are our options then to perform CPU-intensive logic?

  • Put everything on the UI thread and create sluggish apps.
  • Go native, write asynchronous code, create a plugin and consume it from {N}.

Obviously, we'll want to go for the second one. Imagine we have asynchronous plugins for most of the common use cases - like the HTTP cross-platform module or the push notifications plugin. If this is true then the pure application developer would be good to go by just installing the needed plugins. With that said - @NathanaelA, @PeterStaev and every plugin author, we need your help, guys, to isolate these common scenarios and to create plugins to cover them.

I know going native is probably not the most effective approach for plugin authors but it is currently the only available (and viable) one. And I can only assure you that we will definitely have your feedback in mind when prioritizing our backlog.

Btw, @NathanaelA, what about your workaround with utilizing a WebView and using its workers support?

@NathanaelA

This comment was marked as abuse.

@x4080
Copy link

x4080 commented Mar 24, 2016

Love the discussions :)

What I'd like to do is using the native code to do the background processing ex: asynctask in android, and like @enchev said it can be done like the tns notification plugin, my problem is, this process is not very developer friendly, so somehow there's an easy way (maybe like the recent activity android that can be enhanced)

But then again, I dont know very much :)

@PeterStaev
Copy link
Contributor

@atanasovg it is not a good practice to extract everything to a plugin. For example the workflow that @NathanaelA gave above. This is a specific use case for his app. I dont see any benefit in extracting this as a plugin for the masses. Not to mention that i might have some proprietary logic which i do not want to expose as a plugin for apparent reasons ;)

like @x4080 says above - it is not very developer friendly to make every portion of a more resource intensive task in native development environment (android studio or XCode). If I have to open XCode for 50% of the code I have what benefit i have in using {N} instead of just going pure native?

@atanasovg
Copy link
Contributor

@PeterStaev I bet having the SQLite plugin do its work on a background thread would most probably solve @NathanaelA's issues. No need to extract the application logic to another thread. I still believe that there exists a set of plugins that would solve, say 90%, of the application scenarios. And instead of arguing whether this is true or false let's try to compile this list of plugins and discuss case by case.

So far I've heard a scenario related to SQLite data manipulation - well, it is obvious that this is a general purpose plugin that does not have appropriate code and is the ideal candidate for offloading its work to another thread.

@PeterStaev 'n all, please fill the following list with scenarios where you'd need a background thread:

  • SQLite

@PeterStaev
Copy link
Contributor

@atanasovg i already listed my case above but here it is again:

  • Download large binary file, save it to a temp file on the device

@sitefinitysteve
Copy link
Contributor

Personally I would just like a way to "process" something off somewhere else and whatever is handing that be a core part of {N} so I know it's fully tested and rock-solid. I'd be less optimistic about this being done generically by a plugin author who might disappear in 2 months.

@x4080
Copy link

x4080 commented Mar 24, 2016

Yes and plugins is too big for small services that needs async like async .. Task :)

@NathanaelA

This comment was marked as abuse.

@paandahl
Copy link

One scenario for me would be doing data transformations, i.e. iterating on datasets fetched from my backend. A workaround is to do the transformations server-side, but it seems a bit counter intuitive, as the transformations will often be specific to a given view.

A concrete example where background processing made a huge difference in my native code: I am displaying a map, and fetching points of interests inside the area currently displayed. After the POIs are fetched, I need to calculate the display location for each of them, and group them if they are too close to each other. Doing this on the UI thread, will make the map unresponsive and sluggish.

@NathanaelA

This comment was marked as abuse.

@Stavanger75
Copy link

+1
I hope it will be possible to send the reference and not the object between the threads. (for performance)

@x4080
Copy link

x4080 commented Apr 11, 2016

Any plans for blog to develop native plugin for background threading using Android studio and xcode ? Or maybe NS guys can describe how they develop the tns plugin? It will be great time saving

Thanks

@valentinstoychev
Copy link

Hey everyone - In order to explain better the current state of the threading model we wrote a blog post. It also outlines what we are planning for the future and how you can use background threads now.

http://developer.telerik.com/featured/benefits-single-threading-model-nativescript/

@x4080
Copy link

x4080 commented May 11, 2016

nice blog, so the next JavaScript background thread will be able to pass variable from the main JavaScript? and in iOS will it use wkwebview for more performance?

@manojdcoder
Copy link

+1

@valentinstoychev
Copy link

valentinstoychev commented Aug 11, 2016

I'm closing this issue, lets use the other ones for the current state -
NativeScript/ios-jsc#620
NativeScript/android#532

We would love to continue the discussion there on the current thinking!

SvetoslavTsenov pushed a commit that referenced this issue Mar 20, 2019
Use density for clip-path calculations
@lock
Copy link

lock bot commented Aug 29, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked and limited conversation to collaborators Aug 29, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests