JS autowatch issue when used with JS require.

Guillaume DUPONT's icon

Hi,

I use to use the 'autowatch = 1' feature in my JS files in order to recompile them automatically after an edit. Since I use the JS require mechanism to split my JS code in several parts, it seems that the autowatch mechanism gets a bit buggy and I can't figure why... Can you help me ?

The context
I'm currently designing a MIDI editor for a hardware synth with Max for Live. My design is based on an MVC pattern. My model and my controller are pure JS patchers. The controller includes a viewManager.js file (with JS require) to communicate with the view, which is a standard Max patcher. The view contains classical UI objects, the JS code uses a ParameterListener object in order to control the UI objects (set/get). So no patch chords are required to make the controller and the view communicate with each other.

The problem
At dev time, when I edit my code in the viewManager.js and save it, it seems to be not recompiled automatically with autowatch. Indeed, when I change the dial's value in the view, the Max console outputs strange 'ghost' values that look like the previous ones I've used in my code ! And if I edit my code many times, it seems that the Max console outputs the 'history' of all my edits, that is really disturbing...

Please help !
I've built a simple example project with Max 8.1.7 in order to demonstrate the issue I encounter with the JS autowatch property, when used with JS require. I would appreciate if someone could :

• download and unzip my Max example project (Max 8.1.7 required because of the new ParameterListener 'silent' mechanism)
• open the 'jsAutowatchIssue.amxd' project in Max (I'm an Ableton Live user)
• open the 'view' patcher (in blue)
• open the controller.js and the viewManager.js files in an external text editor (I use TextMate)
• have a look at the 'onDialChanged' function in viewManager.js
• do the first and second tests step by step, as described in my comments
• let me know what are the outputs in the Max console

Thank you for your help :)
Regards,
Guillaume

tyler mazaika's icon

Requested access. Low tech solution, but if it's just autowatch that is resulting in problems you could maybe use [filewatch] object and then call compile on your js again.

Guillaume DUPONT's icon

Sorry for the restricted sharing, my Google Drive link should work now. Thank you for the filewatch idea, I'll have a look !

Guillaume DUPONT's icon

I've done some more tests this morning and I start to think the problem is not due to the autowatch option. Actually, if I edit let's say a string in one of my debug post(), the new string appears as expected in Max console after saving the JS files and initing my M4L device. So I'm pretty sure my JS files are recompiled as expected.
The 'history' 'ghost' outputs I get with my code in Max console seems to be in relation with the variable returned by the require() function itself. Apparently require() returns a jsobject, and when I debug it I get a negative value, I guess it's a kind of pointer or address. It looks like a new 'pointer' is created by require() each time I run my code, and that the previous 'pointers' are still active in memory. So their onParameterChange() function may still be active too, which could explain the 'history' output I get. Am I right ? Is there a way to notify the garbage collector to delete these plausible 'previous pointers' ?
I don't know how I can inspect the variable returned by require() a bit more. I know that a getprop() function exists for jsobject, but I don't know what to pass as arguments to get info from the variable...
An idea ?

Jeremy's icon

This is a limitation/bug in the current implementation of jsrequire. We're looking into it, thanks for bringing it to our attention!

Guillaume DUPONT's icon

Hi Jeremy,
Great ! I'm happy you've found the problem with JS require. While an improvement is under study, I will rename my main js patcher each time I'll have to edit my JS subcode, it seems to avoid the problem of multiple ghost outputs from JS require.
Cheers.
Guillaume

Rick Banghart's icon

Guillaume,

I'm doing nearly the same thing you have described. I'm writing an editor for a hardware synth. I have three .js files: one that handles interaction with the synth, one that defines the dictionary of params for the synth, and one that interacts with the UI controls.

I've put post("loaded module v0.1 \n") commands at the top of each .js script. Even though the updated version number in the top line shows up properly in the console when I save, the function I am working on fails to recompile, and my edits are ignored.

I have to close the project and reload it for my edits to be seen.

Rick

Guillaume DUPONT's icon

Hi Rick,

What Max version do you use ? I encountered this issue while I was developing under Max 8.1.7. I've reported the problem to Jeremy @C'74 (see older posts above), and normally the bug has been resolved since then. Have you tried to double click on the name of your main JS patcher, like if you would like to rename it, and then press enter ? This trick seems to force the autowatch mecanism and should help you to reload the last versions of your edited JS patchers.

Guillaume

Rick Banghart's icon

Guillaume,

Thanks for the speedy reply! I was using 8.1.8. I've now installed 8.1.10 and the problem persists.

I tried your suggestion to double-click the name of my main JS patcher. That didn't work, but it led me to try closing only the patcher (instead of the project), then reloading with the double-click on the main patcher in the Project window. That caused the .js to be properly reloaded.

The thing that has me scratching my head is that the autowatch appears to be working (at least partially). That is, in the main body of the .js updated post() commands are reflected in the console "automagically" when the file is saved. That led to me believe that autowatch was working. The problem is that functions within the .js are not reloaded, and fail to produce updated post() commands.

Thanks again,

Rick

Guillaume DUPONT's icon

You're welcome Rick :)

allforabit's icon

Has this been fixed Jeremy? It doesn't seem to reload required files at all for me (working on Windows). The low tech `[filewatch]` and compile approach does work though.

Jeremy's icon

There were two issues in this report. The "real bug" was that this sequence:

  1. Load main.js (requires req.js)

  2. Change something in req.js, save

  3. Resave main.js

  4. The req.js exports still refer to the old version of the file, or maybe to both the old and the new version of the file, or something like that (I don't remember exactly what the symptom was, but maybe Guillaume does).

That bug was fixed in October 2020 and has been in every version of Max/JS since.

The other issue, which isn't what Guillaume reported, but which has come up a few times in this thread, is that there's an expectation that a require should autoload when it changes, even if the requiring JS doesn't change.

This is intentional and is absolutely correct behavior. If you need your top-level js to reload when the require changes, the suggestion to use [filewatch] to trigger a compile to the [js] object is a good one.

I do realize that it's 2024 and people are used to having tools like nodemon to handle this kind of thing outside of the Max context, and I don't think it's a crazy FR to provide some kind of built-in functionality to the JS object to help with this -- we could consider adding something along those lines in some uncertain future.