Do closures work at all in Javascript on Max?
Hi,
I have just started to use Max (5.1.1, for Live) and I notice some irregularities when trying to establish callback functions written in Javascript. It seems that when passing functions to external objects (like LiveAPI), the function is passed by name (or, if I use a literal function, as a string) to the underlying object, invalidating all references to closed-over variables. Now, I can imagine how to work around this restriction by passing the callback arguments through 'this', but that is much less modular and much more error prone than just using closures. Is there any fix for this in the works?
Consider
function record_clip(track)
{
var clip_nr = find_free_clip(track);
function set_clip_length()
{
var live = new LiveAPI(this.patcher);
live.path = ['live_set', 'tracks', track, 'clip_slots', clip_nr, 'clip'];
live.set('loop_end', 4.0);
post('clip length set'); post();
}
var live = new LiveAPI(this.patcher, set_clip_length);
live.path = ['live_set', 'tracks', track];
live.set('arm', 1);
live.path = ['live_set', 'tracks', track, 'clip_slots', clip_nr];
live.property = 'has_clip';
live.call('fire');
}
which generates these errors:
js: livetest.js: Javascript ReferenceError: set_clip_length is not defined, line 148
js: livetest.js: Javascript ReferenceError: track is not defined, line 140
Thanks,
Hans
I fixed some closure issues in jsliveapi post-5.1.1. Send me a PM with your email address and OS if you want to do some testing.
Jeremy
Sorry, just realized that this forum software doesn't support PMs. Send me an email at jeremy@cycling74.com with that info, if you're interested.
As I previously documented on this forum, closures don't work in callbacks only.
There's a simple workaround though!
// Tasker is a class with a single method, Run, that applies the given function
// with the given "this" and args.
//
// Tasker works around the issue documented in
// http://tinyurl.com/max-js-task-destroys-closures.
function Tasker(object, method, args) {
this.object = object;
this.method = method;
this.args = args;
this.running = false; // Perfect for adding a listener to!
this.Run = function() {
this.running || this.Execute();
};
this.Stop = function() {
if (this.running) {
this.task = this.task && this.task.cancel() && false;
this.running = false;
}
};
this.Execute = function() {
this.Stop();
this.method.apply(this.object, this.args);
};
// Schedule this.Loop to run after a delay.
this.Schedule = function(delay) {
this.task = this.NewTask();
this.task.schedule(delay);
this.running = true;
return this.task;
};
// This method is overriden in tests.
this.NewTask = function() {
return new Task(this.Execute, this);
};
};
The canonical URL for this code is http://www.swirly.com/Max/js/util/tasker.js
Hi
Maybe check out the "net.loadbang" stuff - I think there is a Groovy instantiation that may help you out....
Cheers