Really like this! This sort of work would really simplify using things like Flucoma. Generally, for a language that revolves so much around server/client synchronisation, I think that asynchronous methods are lacking.
However, I’d prefer to see a proper futures/promise implementation.
I’ve been using a sort of bodged system, similar to yours but not as clear, that lets you chain these together using the >>=
operator, it looks looks like this…
(
~do_something = {|v| postf("the result is: %\n", v)};
fork {
~futureA = Future({1}) // must be a function or another future
>>= (_ + 2)
>>= {|value, return| postf("waiting for %\n", value); value.wait; return.("hello")}
>>= (_ + "mum")
>>= _.split($ )
>>= {|value, return| "wait for 1".postln; 1.wait; return.(value ++ ["I'm"])}
>>= (_ ++ ["egg"]);
"A".warn;
~futureA.dispatch(); // will dispatch but not wait
// do something else here...
"B".warn;
~futureB = Future(~futureA) >>= _.swap(1,3) >>= {|value, return| 3.wait; return.(value)}; // does not wait
"C".warn;
postf("c is %\n", ~futureA.value()); // waits for futureA
~do_something.(~futureA.value()); // does not wait as already calculated
"D".warn;
~futureB.value().postln; // waits for futureB
}
)
Here’s the implementation … its really dumb and requires the arguments of the function to be value
and return
in that order or it won’t work… I kinda gave up on it so its a little hairy…
Future {
var functionSequence, <>finalValue, dispatchCond;
classvar <>defaultTimeOut=10;
*getFuncType {
|f|
var args = f.def.argNames;
if(args.size() == 2, {
if( (args[1] == \return) && (args[0] == \value), {^\returnWithValue})
});
if(args.size() == 1, {
if(f.def.argNames[0] == \return, {^\return}, {^\normal});
});
if(f.def.argNames.size() == 0, {^\normal});
^\invalid;
}
*new { |f| ^super.newCopyArgs(if(f.isKindOf(Function), [f], {[f]} ), nil) }
dispatch {
dispatchCond = CondVar();
fork {
finalValue = functionSequence.inject(nil, {
|prev, nextFunc|
case
{nextFunc.isKindOf(Function)} {
switch(Future.getFuncType(nextFunc),
\returnWithValue, {
var cond = CondVar();
var done = false;
var result;
nextFunc.(prev, {|return| result = return; done = true; cond.signalOne; });
cond.waitFor(Future.defaultTimeOut, {done});
result;
},
\return, {
var cond = CondVar();
var done = false;
var result;
nextFunc.({|return| result = return; done = true; cond.signalOne; });
cond.waitFor(Future.defaultTimeOut, {done});
result;
},
\normal, { nextFunc.(prev) },
{"invalid func".error}
)}
{nextFunc.isKindOf(Future)} {nextFunc.value()};
});
dispatchCond.signalOne;
}
}
value {
if(dispatchCond.isNil, {
this.dispatch()
});
dispatchCond.wait({finalValue.isNil.not});
^finalValue;
}
>>= { |f|
if(Future.getFuncType(f) != \invalid, {functionSequence = functionSequence ++ [f]});
^this;
}
}