Prev: Module app: Application Control
Module async: Asynchronous Function Streams
This module provides functions to create and set up streams which turn an asynchronous function call into a read from a stream: when the asynchronous function completes, the value it returns can be obtained from the stream.
By mapping asynchronous function calls into stream reads and waiting for data being available via io.wait, a "parallel" wait for the completion of multiple asynchronous functions becomes possible, without resorting to multiple processes and pipes (see module proc).
The typical steps in setting up asynchronous function streams are:
- Create the stream(s) via async.new.
- Call the function(s) via async.call, passing the function to be called as a reference.
- Use io.wait to wait for completion of any of the functions, or for another stream having data to read.
- Abort the calls as required via async.abort to free up the modules for other calls (see "Restrictions" below for details).
- Depending on the type of stream returned by io.wait, get the function result via async.result, or simply read the available data.
- Call the functions which completed or were aborted again via async.call.
Steps 3 to 6 form an event loop.
- When they are no longer needed, close the streams with io.close. This also aborts any pending calls on the streams.
For instance, a single process can simultaneously wait for an incoming SMS, the user pressing a key, and a bluetooth connection being made:
// setup for UI and Bluetooth
ui.keys(false);
service=bt.start("MyService");
// create three streams
s=[async.new(),async.new(),async.new()];
// call the asynchronous functions
async.call(s[0], &sms.receive);
async.call(s[1], &ui.cmd);
async.call(s[2], &bt.accept, service);
while true do
// wait for any of the functions to complete
t=io.wait(s);
// read the function result
v=async.result(t);
case t
in s[0]: // v is an SMS
...
// receive next message
async.call(s[0], &sms.receive)
in s[1]: // v is a keycode
...
// get next keycode
async.call(s[1], &ui.cmd)
in s[2]: // v is a BT connection
...
// wait for next connection
async.call(s[2], &bt.accept, service)
end
end;
// close the streams
for t in s do
io.close(t)
end
|
Restrictions
There are three important restrictions to observe when using module module
async:
- Only calls to native functions can be turned into stream reads. As all low level asynchronous functions are implemented natively, this normally does not pose a problem.
- Native m modules are generally not reentrant, so no new function call can be made while a call of the same module is still pending. For instance, you cannot send an SMS via sms.send while an sms.receive call is still pending. Attempting to call a module with a pending call throws ExcModuleBusy.
- Any explicitly set timeouts of the asynchronous function is ignored. Only the timeout set via io.timeout is observed.
async.abort
• function abort(stream) → null
Aborts any pending function call on stream; does nothing if there is no pending call.
async.call
• function call(stream, function, ...) → null
Calls the function referenced by function, passing the remaining parameters, and returns immediately. As soon as function returns, a byte becomes available on stream, and the function result can be gotten from stream via async.result.
Any timeout on function is ignored, whether it is part of the function's module, or a parameter of the call. For functions called via async.call, timeouts must be implemented via io.timeout, and the pending call explicitly aborted via async.abort, if required.
If the call immediately terminates with an exception, the exception will appear to be thrown by asynccall>. If a call terminates with an exception asynchronously, the exception will be thrown when obtaining the result with async.result.
Throws ExcNotFunction if function is not a function reference. See also section * (Reference).
Throws ExcNotNative if function is not a reference to a native function.
Throws ExcModuleBusy if the function's module already has a pending call.
Throws ErrInUse if the stream already has a pending call.
// two functions performing essentially the same
function synchronous()
return phone.state(phone.idle)
end
function asynchronous()
// create a new stream
s=async.new();
// call phone.state(), returning immediately
async.call(s, &phone.state, phone.idle);
// wait for phone.state() to complete
io.read(s, 1);
// get the result of phone.state()
state=async.result(s);
// close the stream
io.close(s);
return state
end
|
async.new
• function new() → Native Object
Creates a new asynchronous function stream accepting function calls and returns it. The returned stream can only be used for asynchronous function calls. It cannot be written to, and a single byte can be read after an asynchronous function call completes.
// create an asynchronous function stream
stream=async.new()
|
async.pending
• function pending(stream) → Boolean
Returns true if stream has a pending call. Returns false if there is no pending call.
Use io.avail to determine whether a result is available.
// start a new call if there is none pending,
// and there is no result to be read
if not async.pending(s) and io.avail(s)=0 then
async.call(s, &sms.receive)
end
|
async.result
• function result(stream) → anytype
Get the result of the last function called via async.call.
Throws ErrNotReady if there was no call or if it is still pending.
Next: Module proc: m Processes© 2004-2011 airbit AG, CH-8008 Zürich
Document AB-M-LIB-888