notifydeleted() called on [bpatcher] reinstantiation

Chris's icon

I’m using some javascript to manage various things inside a [bpatcher]. To keep this tidy I’m using the notifydeleted() method to clean up when the [bpatcher] is deleted.

I notice that notifydeleted() runs whenever the [bpatcher] is reinstantiated (for example when its arguments are changed using the inspector). That makes sense, but in such cases (rather than a straight-forward deletion) notifydeleted() seems to be running after the loadbang() method.

This means that some of the initialisation procedure is annulled by an immediately following clean-up routine. Any ideas on whether this is the expected behaviour and how to get around it? Am I misunderstanding something?

EDIT: To be more specific, what seems to be happening is that upon reinstantiation, the new instance’s loadbang() is called, and then the old instance’s notifydeleted(). So anything called within those methods that affects global (outside JS) set-up can easily conflict.

Chris's icon

In a similar vein, does anyone know if there’s any way to distinguish between a reinstantiation event and an actual deletion? Seems like a useful thing to know.

Chris's icon

Anybody? Still wondering how to deal with this order problem.

Jesse's icon

I can confirm that this is apparently happening in a simple test on my laptop using Max7. I agree that it's odd that this would be the case.

Without seeing your specific code it's hard to comment on the best approach. You could perhaps use a boolean within your javascript to prevent notifydeleted() from reinitializing your global variables, which would then be reset after a delay via the Task object, or perhaps reset via an external trigger via the closebang object?

Chris's icon

Thanks for the suggestion of the Task object — I had been considering it, so I’ll head in that direction and see if it can work.

The full project is kind of complicated, but can be found in its entirety here if you do want to dig around in it: https://github.com/delucis/cs.2click

To summarise the specific issue:

  1. I’m using a [dict] to share information between multiple instances of a [bpatcher]

  2. When a [bpatcher] is initialised, it sets various dictionary keys (called by a loadbang() method)

  3. When a [bpatcher] is deleted, it should remove its associated keys (but not if we’re reinstantiating evidently)

Here are the relevant set and tidy functions called by loadbang() and notifydeleted() methods respectively (they’re using some global variables, sorry if the code is unclear):

// setdict -- adds all slots found in this patcher to the global dictionary
function setdict(val) {
    // iterate through slots and add/set them in the dictionary
    for(s=0;s
Emmanuel Jourdan's icon

AFAICT, it's the expected behavior, or behaviour if you prefer ;-)

What's happening is when you change the arguments to the bpatcher object, it loads a new instance of the patcher with different arguments (which is why your js's loadbang starts up), and then free the old one (which causes the notifydeleted method of the old bpatcher to fire). You can see this by having a specific identifier for each instance of js as in the attached example.

jsnotifytest.zip
zip
Chris's icon

Hi Emmanuel, thanks for the confirmation that that’s the expected behaviour. I still think it’s a little odd, but OK, good to know I have to work around it.

I’ve found a solution using a task to set flags on loadbang(), clearing them 100ms later (by which time notifydeleted() has already run if we’re reinstantiating) and a [dict] to store them. This works because my problem is only when there are persistent IDs across instances — so this filters out the notifydeleted() method if the ID matches the flag set on loadbang().

In case anyone is curious, here’s the code: https://gist.github.com/delucis/ee01a5a0c8a49fb8cb6a

Does this seem like a reasonable way to do this? I’m hesitant about the fact it relies on delays, but it seems about the best possible solution.

Attached an adapted version of EJ’s patch with the above javascript.

jsnotifytest-2.zip
zip
Emmanuel Jourdan's icon

I wouldn't add more asynchrony than there is already ;-) I'd just write something in another dict that can be checked at loadbang to know if you need to remove thing from the other js. Nevertheless if you use a Task, make sure you declare it with the var keyword, in the global code.

Chris's icon

Oh, OK, thanks, I streamlined it to this — didn’t realise the task should be declared globally, but that makes sense.

// globally declare task
var runFlagsOff = new Task(flagsOff, this);

function flagsOn() {
    // add this module as flagged in the dictionary
    twoclickFlags.set(modulename, 1);
    // run flagsOff() 100ms later
    runFlagsOff.schedule(100);
}

function flagsOff() {
    // remove this module from the dictionary
    twoclickFlags.remove(modulename);
}

I can imagine a method like you suggest using randomly generated IDs to avoid the asynchronicity but is there any way to guarantee no conflicts in ID number? An URN style generator that runs across js instances? I know your Math.round(Math.random()*100000) method gives an infinitesimally small likelihood of conflicts, but am I really OK to rely on that?

(That’s small-fry anyway, thanks so much for the help — at least at this point it works!)

Emmanuel Jourdan's icon

You could use a f="https://docs.cycling74.com/max7/vignettes/jsglobalobject">Global object to store the ids and make sure that they are uniq. Or you could just pass an argument to your js object, like #0-id which will take care of the uniqueness.

Chris's icon

Thanks again for your help with this — finally got around to implementing it!

For any future reader, the current basic framework I’m using for handling deletion and re-instantiation events is here: https://gist.github.com/delucis/ee01a5a0c8a49fb8cb6a