What is the best way to delay running of the bulk of code for a ClojureScript webapp until a certain condition has been met? In case it matters, the condition is a certain DOM element losing focus (and being non-empty), or it could be a POST action but I haven't implemented it that way and may or may not do so. I've finished the first working version of a little webapp in ClojureScript, but the last step I implemented was having the program wait until the user has entered something into a text field (a seed for random number generation, in case that matters) and then clicked outside of that field (or otherwise caused it to lose focus). (I would also like to verify that the text field is non-empty, but I haven't implemented that yet. To just get things working, what I did was just this nesting:
(.addEventListener
(.getElementById js/document "text-field-seed")
"blur"
;; most code here
)
That feels like a kludge, though, because right after I did that, my text editor gave me a huge number of inline def [inline-def] flags (one for every (def [...]) and (defn [...]) line, and maybe more).
I gather that that type of warning is to prevent running (def [...]) and (defn [...]) forms, and maybe other things, more than once. In my scenario, though, this is not much of a worry (and I may even implement making the text field impossible to gain focus again after losing it). Would it be completely fine to just ignore those flags, and keep the code as above, with the nesting, or would there be a better way to do this?your defn should not contain anything that breaks if the page is not loaded yet. that can mean taking something that is being grabbed from global (window) context and instead providing it as an argument
the overall pattern is moving all read or write of state to the "edge" of the code - providing it as an argument or reading it from a return value at the top level, so that every function but one can be "pure"
Regarding code splitting - it could be useful here, but not clear. It's usually used when you don't want to load a huge piece of code until you know you really need it. Has nothing to do with state or delayed execution per se.
If your defs don't depend on the state of the input, just move them to the top level. Keep inside the event listener only the things that really depend on that event.
As noisesmith said, try keeping as many functions pure as possible. If you have some state under defs that depends on that input field, move that state to a mutable storage - usually an atom.
That way, all the definitions will be independent from user actions. And user actions will only change the state, not re-define things.
There is a fair amount of defs and especially defns that don't need to be delayed (they are independent of the value in the text field), but I like the idea of not running most of them right away anyway. They can all run after the text entry.
@sam.t.straus It seems like the goal of code splitting is to speed up loading. I do think it would be good to learn at some point, but I don't see how it would be useful for my case here. Are you saying a piece of code that triggers running a separate module ends up waiting until that module is done running in order for it to continue running?
@noisesmith When you write "your defn", what defn do you mean? Just any defn? Meaning all of them? As for moving read or write of state to the 'edge' of the code, I think I follow, but I do already have anything subject to change as atoms.
I mean no defn should be inside another form - they should all be top level forms
@p-himik Yes, in my case every bit of my code will be needed, so it does sound like modules don't really address my wait-for-some-condition situation. Yes, there are certainly a number of defs and especially defns that I could move to before the top level. All intentionally mutable state is stored in atoms already, though I do understand that an "inline def" or defn means that def or defn could be unintentionally triggered to run more than once (if, say, in my case, a user were to edit text in the text field and click outside of it, and then do that all again) Trying to make that unlikely thing actually impossible is what I'm trying to figure out how to do. I could make the text field unable to ever be re-entered once left, but while that would make re-running the defs and defns impossible I'm sure my editor would still complain about inline defs.
one or more can acquire some stateful resource, then pass it to others, but none should be unsafe to exist outside state
Maybe I should explain a alternative I was thinking about but didn't go with: Having a while form for while the seed text field is in focus, maybe with some small time delay that gets executed every time through, thus having no execution of anything after that point until the text field is no longer in focus. If that worked, then I wouldn't need to put anything at all in my seed text field event listener. It could all just follow that while form. Would that be a good approach?
I mean no defn should be inside another form - they should all be top level formsThat was the case for 99% of the time I was working on this. I just placed the code above around the bulk of it at the end to delay until that condition check. I do want to avoid it, but I'm not sure how (other than the
while idea I just wrote above).Essentially there are 3 kinds of code I have:
1. Code dealing with the condition (the code in my original post, or the while block I just described, or something else I have yet to learn to do).
2. Code that is directly dependent on text in the text field (such as a defs that act downstream of the text entered into the field, seeding a random number generator and doing things with random numbers).
3. Code that is either not dependent on the text in the text field, or not very dependent on it (only its side effects are) (such as code that takes in user key presses and either acts on those alone or acts on those along with things downstream of the random number generation).
Right now I have most of 2. and 3. inside 1.
I realize I may be able to move code to be delayed to instead after 1. as per my while idea if blocking code like that works (and is idiomatic?).
I realize I could move 3. to before 1. since that code doesn't need to be delayed, but that still leaves the question about what to do with 2. I can't generate random numbers and work downstream of them if no seed text has been entered yet.
(I also do actually like delaying not just 2. but also 3, since I like delaying the drawing of the majority of the UI (part of 3.) to avoid distracting the user from the seed text field, and I certainly want to delay capturing user key presses until after the text field has been filled in.)
For my while idea in particular, I would assume that would work, since JavaScript at least by default is single-threaded, but I'm not sure because I've read that it is somehow also asynchronous?!
> Having a while form for while the seed text field is in focus [...]
> Would that be a good approach?
This wouldn't work at all.
First of all, even if it could work, that's just a bad approach. There is no need for busy looping in 2026, unless the underlying system requires you to do so.
Second of all, JS is single-threaded by nature. You cannot block its execution and expect other things to work - they share that thread. You can't even make time delays in regular imperative calls, you can only schedule executing a particular function till some time later, without blocking the execution. And if you do block the execution with something akin to (while @loop?), your blur event handler won't ever run - nothing will ever run anymore, except that loop, because it wouldn't have a chance to run. The page will be stuck in the loop, till you close the page.
There are some exceptions, but they aren't applicable here.
> Code dealing with the condition
It should be moved to its own functions that you call from inside the event handler.
> Code that is directly dependent on text in the text field
It should also be moved to its own functions that are called with the text as an argument.
Don't move the code to be delayed. Define everything at the top level, make only the state setting and function execution happen after the input field is blurred. It doesn't matter if the state then should be read-only - "define with a value" and "define with no value and set the value exactly once" are identical if everything that depends on the value runs after the value is set.
> I like delaying the drawing of the majority of the UI
If you approach UI as a function of state, it would be just setting a particular state to a particular value and then calling a side-effecting render function, that's it.
> You can't even make time delays in regular imperative calls, you can only schedule executing a particular function till some time later, without blocking the execution.
So if I have (js/setTimeout (fn [] (js/alert "Delayed alert!")) 500) that 500-millisecond delay only affects the alert, and does not affect lines of code below it? That is really good to know.
> [Code dealing with the condition] should be moved to its own functions that you call from inside the event handler. In my case the event handler is the condition, but I think I know what you mean. For instance if I want to check that the text field has lost focus and is non-empty.
> 500-millisecond delay only affects the alert, and does not affect lines of code below it Indeed, because it's not a delay - it's a scheduled execution. > For instance if I want to check that the text field has lost focus and is non-empty. This is what I meant:
(def state (atom nil))
(defn handle-input [input-value]
(reset! state {:init-value input-value})
...)
(.addEventListener
(.getElementById js/document "text-field-seed")
"blur"
(fn [evt]
(let [value (-> evt .-target .-value)]
(when (seq value)
(handle-input value)))))
As simple as that. The handler does exactly what you described, but nothing more - everything else handled by functions defined outside of it.A couple of things worth mentioning: 1. You might want to use some UI library, if your UI is even a tiny bit more complex than a few inputs and a form. Listening to blur could be something nice, like
[:input {:type "text"
:on-blur (fn [evt] ...)}]
2. If you do use (.addEventListener ...) at the top level, if will get in the way of code reloading - the addition of the listener will happen on every namespace load. So you'd have to wrap it in defonce with a bogus var, just so that code block is not re-executed.> If you do use (.addEventListener ...) [...] wrap it in defonce with a bogus var,
This is just standard practice for ClojureScript?
It does make sense, and actually I have run into odd cases of keyup events being processed twice, which totally look like what you described (somehow there ending up being two instances of the keyup event handlers in play at once, maybe due to the text field losing focus twice) and which could be solved with the fake var defonce trick..
Standard practice when you have something side-effecting and namespace loading time. Which by itself isn't standard practice. :)
I'm not sure what you mean by that. Most of the functions I want to delay calling do happen to have side effects, but I would still have the question even if they did not have side effects.
If I have a function which depends on the value of var a, and don't want to call it until a is ready, to me that seems like a pretty basic use case. Is it not?
As for namespaces and loading times, I don't understand that part of what you wrote at all.
> [Code that is directly dependent on text in the text field] should also be moved to its own functions that are called with the text as an argument. > Don't move the code to be delayed. Pretty much everything is already var definitions and function definitions, so I'm not sure what you mean by not moving code. Was that referring to if I were just executing side-effect-generating the top level? (Which I'm not, since for instance I'm always calling functions that do drawing to the screen) > Define everything at the top level, make only the state setting and function execution happen after the input field is blurred. It doesn't matter if the state then should be read-only - "define with a value" and "define with no value and set the value exactly once" are identical if everything that depends on the value runs after the value is set. Having function execution after the input field is blurred is the situation I am trying to code properly. I have moved a large amount of code to outside (before) the text field blur event handler code---everything that didn't depend in any way on the text in the text field, i.e. anything that didn't depend on the random number generation---but I'm still left with some code that has to run afterward and I don't see how to avoid inline ref warnings for that.
I meant specifically adding the event listener - I assume it happens at the top level, which isn't great. The fewer such things there are, the better. Ideally, just the fact of loading a namespace should not change any behavior. Of course, it's not always feasible (especially if you use multimethods or protocols), but at the very least loading a namespace should be idempotent.
Trying again to get your namespace point...
I don't see when I'm loading a namespace in my case. Do you mean hypothetically if I were to load a new namespace, maybe somehow one that overrode the value of a to something else?
If that were to happen, then yes I can see that with the first way I wrote the code in the first post, that change of value of a would change the way the function runs and change the results of that function in general (for any call of it), while with the second way , that change of value of a would not change the way the function runs in general and would only change the result of the function for the call in the code (passing a) in particular.
> I don't see when I'm loading a namespace in my case.
1. When you start the app, everything that you :require is loaded
2. When you use automatic code reloading, namespaces that you change are reloaded automatically