-
Notifications
You must be signed in to change notification settings - Fork 89
Enable semantics similar to java.lang.Thread
?
#8
Comments
Hey Alex. I'm also using Clojurescript, and it's working fine for me. Is there anything I can help you with (besides writing a new interface)? |
Thanks so much for commenting and being so helpful @seantempesta ! Glad to know there's someone out there using ClojureScript with React Native and WebWorkers :D Basically for now I'm trying to get it to work with Figwheel to work with it (I'll figure out the production part later, if there is indeed anything additional to figure out). I'd like to use it with the CLJS Servant library (for WebWorkers in the browser) do the following currently (new semantics aside):
I've tried using a separate worker.js file which |
I haven't figured out a way to get the worker to run in figwheel. I'm compiling the worker to a separate
and am loading it by a manual patch to WorkerManager.m:
Here's a rough version of my code (let me know if you want the full version?): worker.js (ns project.worker)
(def Self (.-self (js/require "react-native-workers"))
(defn on-message [transit-message]
(let [message (t/read tr transit-message)
operation (first message)
args (first (rest message))]
(info "WORKER: Message received:" (str operation))
(case operation
:subscribe (receive-subscription args)
:dispatch (receive-dispatch args)
:dispatch-sync (receive-dispatch-sync args))))
;; init and send a message to the main thread when ready
(aset Self "onmessage" on-message)
(let [ready-transit-m (t/write tw [:worker-ready])]
(.postMessage Self ready-transit-m)
(info "WORKER: Ready")) And then in my normal project.ios.core file: (defonce Worker (.-Worker (js/require "react-native-workers")))
(defonce worker (atom nil)) ;; worker ref stored here
(defonce worker-ready? (r/atom false))
(defn on-message [transit-message ready-fn]
(let [message (t/read tr transit-message)
operation (first message)
args (first (rest message))]
(trace "MAIN: Received message from worker" operation)
(case operation
:worker-ready (ready-fn)
:worker-logging (receive-worker-logging args)
:reaction-results (receive-reaction-results args))))
(defn init-worker [worker-file ready-fn]
(debug "MAIN: Starting worker")
(let [worker-init (Worker. worker-file)]
(aset worker-init "onmessage" #(on-message % ready-fn))
(reset! worker worker-init)))
;; Start the worker
(init-worker "worker.js" (fn [] (reset! worker-ready? true)))) So you might be thinking that this would be a horrible dev environment? It would be if I had to re-compile, bundle and restart the simulator on every change, so I just wrote an abstraction library that passes the calls directly whatever functions the worker is using (in the case, re-frame ;;;;;;; API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; When in production pass all re-frame requests to the worker
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce use-worker? (atom false))
(defn dispatch [dispatch-v]
(if @use-worker?
(worker-utils/dispatch dispatch-v)
(re-frame.core/dispatch dispatch-v)))
(defn dispatch-sync [dispatch-v]
(if @use-worker?
(worker-utils/dispatch-sync dispatch-v)
(re-frame.core/dispatch-sync dispatch-v)))
(defn subscribe [sub-v]
(if @use-worker?
(worker-utils/subscribe sub-v)
(re-frame.core/subscribe sub-v)))
(defn init-worker [worker-file ready-fn]
(reset! use-worker? true)
(worker-utils/init-worker worker-file ready-fn)) I don't know if you're using re-frame, but this worked tremendously well for me. I just keep all of my heavy computations in the subscriptions and handlers and the main thread just renders the results. |
Thanks for your detailed and helpful reply @seantempesta ! Yes, I'm using re-frame (with Reagent). I think it's a really great idea to pass (most of) the dispatches to a worker! That makes the separation of concerns really clean and has the potential to drastically improve performance (provided that it's not a super simple dispatch like a an However... what do you do when you modify code in development/Figwheel that is used by the worker thread, whether exclusive to it or shared with the main thread? I guess what you were saying is that since you haven't gotten workers to run in Figwheel, then you'd probably just set the I mean, it's great to hear that it runs in ClojureScript! Don't get me wrong about that. I think your solution is the most elegant one possible given the constraints of Figwheel and the fiddly nature of workers (which require a separate JS VM and a worker-specific .js entry point) as compared to e.g. Java threads which share context. It still just seems far less fluid than would be optimal for rapid development. What do you think? |
So I did some digging around and saw that react-native-workers uses the React Native JSBundleLoader class in Android to load the worker .js file(s) (I don't really know Objective-C super well so I haven't looked at that side of things, but I assume it works roughly the same way). Since it ultimately works the same way as WebWorkers (that is, to create a different Thread, you have to load a .js file), I'm thinking of this as a rough pseudocode solution:
What do you think, @devfd ? Meanwhile... for Figwheel's sake @seantempesta we could have some sort of additional service/middleman (or maybe modify Re-Natal or something?) which, every time the bundle is changed, outputs a JS worker bundle which at the top says something like |
@alexandergunnarson: These are all good questions (most of which I don't have answers to). However, I finally got around to packaging the code I've been using into a library. I think you might find it useful. I put an example in there that runs re-frame normally during development (where it's okay if it's slower) and then uses the worker in production. That seems to be the easiest way to use this library with chrome debugging ATM. |
That's awesome @seantempesta ! Thanks for sharing. I'll take a look at what you wrote and try it out. Your solution is probably the closest we ClojureScript people are going to get until the pseudocode I wrote (along with the extra service/middleman I mentioned) is implemented, if it ever is. I think I'm going to at least try implementing it sometime soon. |
I've been having ridiculous problems with trying to get this to work in a non-headachy way, not through any fault of your own. This is a great library and I appreciate your work! But I'm using ClojureScript (a compile-to-JS language) and due to its fancy use of Google Closure and other such things, I'm not able to get Workers to work in an easy way (I can use them on the web just fine with ClojureScript). I'd love to have this library support (in addition to the existing WebWorkers semantics, because I suppose some people want that)
java.lang.Thread
-esque semantics like this:I'm thinking that under the hood
Thread
might use the native bridge to spin up a JS VM and load all the JS code that the current context has access to (yes, this part would be hard), and then inject a booleanThread.isMainThread = true
(I'm not sure if this is possible but it if it is, it seems like it would be simple and it would be dang nice).Thoughts on feasibility?
I'd love to contribute to help make this possible.
The text was updated successfully, but these errors were encountered: