Skip to content
JeanHuguesRobert edited this page Apr 25, 2013 · 11 revisions
l8
   -- step/task creation. "body" can create additional steps/subtasks
  .step(   body )     -- queue a step on the path to task''s completion
  .task(   body )     -- queue a step that waits on a blocking subtask
  .fork(   body )     -- queue a step that starts a forked task, forks "join"
  .repeat( body )     -- queue a step that repeats a blocking subtask
  .spawn(  body )     -- like fork() but next step does not wait for subtask
  .generator( body )  -- queue a step that spwans a task that yields results

  -- step walking
  .proceed( block )   -- walk a step on its path, at most once per step
  .walk               -- idem but params of block become results of step
  .flow               -- idem but first param is filtered out unless thrown
  .continue           -- stop executing current task, reschedule it instead
  .break              -- "break" for "repeat" steps
  .return( [val] )    -- like "return" in normal flow, skip all queued steps
  .raise( error )     -- raise an exception in task, skip all queued steps

  -- task completion monitoring, for task users
  .then( ... )        -- Promise/A protocol, tasks are promises
  .callback( cb )   -  - Node.js style callback. Also .callback( promise, cb)
  .join()             -- pause task until all subtasks are done

  -- task completion handling, for task implementers
  .defer(    body )   -- push a block to execute when task is almost done
  .progress( block )  -- block to run when a subtask is done or step walked
  .success(  block )  -- block to run when task is done without error
  .failure(  block )  -- block to run when task is done but with error
  .final(    block )  -- block to run when task is all done (after .defer())
  
  -- task "local" variables, subtasks inherit them, a binding store them
  .var( name, val )   -- define a new variable in current task''s binding
  .get( name )        -- get value of task local variable
  .set( name, val )   -- set value of task local variable
  .binding( name )    -- return binding where task local variable is stored

  -- task state related
  .state              -- return state of task, I->[Run|Pause]*->Success/Fail
  .pause              -- block task at step, waiting until task is resumed
  .paused             -- return true if task was paused
  .resume             -- resume execution of task paused at some step
  .running            -- true if task not done nor paused
  .cancel             -- cancel task & its sub tasks, brutal
  .canceled           -- true if task failed because it was canceled
  .stop               -- gentle cancel
  .stopping           -- true after a gentle cancel, until task is done
  .stopped            -- true if done task was gently canceled (gracefull)
  .done               -- true if task done, else either running or paused
  .succeed            -- true if task done without error
  .fail               -- true if task done but with an error
  .error              -- last raised error (ie last exception)
  .result             -- result of last successful step
  .timeout( milli )   -- cancel task if it is not done on time
  .sleep(   milli )   -- block on step for a while, then move to next step
  .wait( promise )    -- block task until some lock opens, promise agnostic

  -- misc, hierarchy
  .l8                 -- return global L8 object, also root task
  .current            -- return current task
  .parent             -- return parent task
  .tasks              -- return immediate pending sub tasks
  .top                -- return top task of sub task (child of l8 root task)

  -- scoping (value of "this" related)
  .begin              -- create a new task
  .end                -- start that new task
  .Task( function )   -- the .begin/.end guarded version of a function

All these methods, if invoked against the global l8 object, will usually get
forwarded to the "current task", the task that is currently executing. That
task is often the returned value of such methods, when it makes sense.

To synchronize the access to resources, l8 provide a few well known basic
solutions implemented using promises and invoked using task.wait( resource):

.semaphore( [n] )     -- create a new semaphore, also a promise provider
.mutex( [entered] )   -- ... a new mutex, also a ...
.lock( [nentered] )   -- ... lock (reentrant mutex), ...
.queue( [bound] )     -- message queue, ...
.port()               -- like a message queue but without any buffering
.signal()             -- signal, ..., like a promise that fires many times
.timeout( delay )     -- a promise fulfilled within a delay
.generator()          -- a next()/yield() consumer/producer resource
.Generator( block )   -- build a Generator Constructor.

Semaphores, mutexes and locks provide:

  .promise            -- provide a promise fullfilled when rsrc is acquired
  .release()          -- make resource available
  .signal()           -- alias for release()
  .close()            -- reject pending promises
  .task               -- resource owner task, when applicable (mutex & lock)

Message queues are useful to synchronize a consumer and a producer:

  .in                 -- a "can get()" promise, alias for .promise
  .out                -- a "can put()" promise
  .get()              -- pause current task until queue is not empty, get msg
  .tryGet()           -- get msg when one is available, do not block
  .put( msg )         -- pause current task until queue is not full, put msg
  .tryPut( msg )      -- put msg in queue unless queue is full
  .signal( msg )      -- alais for tryPut()
  .capacity           -- total capacity (bound)
  .length             -- used capacity
  .full               -- when capacity is totally used
  .empty              -- when length is 0

Timeouts are convenient to measure time and detect excessive delays.

  .promise            -- provide a promise fullfilled withing the delay
  .signal()           -- fire the timeout now
  .started            -- time when the timeout was started
  .signaled           -- time when the timeout was signaled, or null
  .duration           -- how long it took (took so far if unsignaled timeout)

Signals are usefull to send a signal to multiple tasks when some condition is
met:

  .promise            -- a promise fullfilled when signal is next signaled
  .signal( value )    -- signal signal, resolve all pending promises

"Calls" are functions that will be called when signaled. They are similar to
regular callbacks. The main difference is that in addition to .apply() and
.call(), Calls also provide a .signal() method, like all the other l8 objects
that are usefull for synchronisation purposes. Another difference is the fact
that Calls are asynchronous, their result is a promise.

  .promise            -- provide the promise of the call.
  .call( ... )        -- invoke the call with parameters
  .apply( a )         -- idem but parameters are specified using an array
  .signal( ... )      -- alias for .apply()

Generators let a producer and a consumer collaborate in a next()/yield() way:

  .get                -- a "can next()" promise, alias for .promise
  .put                -- a "can yield()" promise
  .next( [msg] )      -- pause task until producer yields, get/send a msg
  .yield( msg )       -- pause task until consumer calls .next(), get/send
  .tryNext( [msg] )   -- if .get promise is ready, get yield's msg
  .tryYield( msg )    -- if .put promise is ready, get next's msg
  .signal( msg )      -- alias for tryYield()
  .close()            -- break paused tasks (using .break())
  .closed             -- true once generator is closed

When a producer task is created using a Generator Constructor, that task can
use l8.yield() while the parent task can use l8.next() ; the associated
generator will automatically get closed when either the producer or the
consumer task terminates.

Many actions are possible when you have a hand of promises, l8 provides some
of them:

.selector( promises )  -- fires when any promise does
.any( promises )       -- alias for .selector()
.or( promises )        -- fires when a promise with a non falsy result fires
.aggregator( promises) -- collect results, fires when all promises did
.all( promises )       -- alias for .aggregator()
.and( promises )       -- fires with "false" early or with collected results

Note: in addition to promises, the array can contain immediate values and
Functions returning either an immediate value, a function to evaluate or a
promise. The result of a promise can be a Function that will be evaluated and
will replace the initial promise.

Additional librairies provides other usefull services. See Q.js, When.js,
Promise.io, etc.

-- Actors runs in places called "stages" --
They are remotely accessible using proxies.

.actor( name, pattern ) -- start an actor or return an actor generator
.actor( name )          -- look for an existing actor
  .tell( ... )          -- send a message to the actor
  .ask( ... )           -- send a message and expect an answer
  .receive( pattern )   -- actor redefines reaction to received messages
.ego                    -- actor the current task is running
.ego.stage              -- stage the actor received current message from
.stage( name, [url] )   -- a place with actors in it
.stage( "local", srv )  -- define http server for local stage
.proxy( name, url )     -- access to remote actors
.proxy( name, stage )   -- access to browser side remote actors
  .tell( ... )
  .ask( ... )

Misc:
  .debug( [on])       -- get/set debug mode
  .trace( p1, ... )   -- output trace
  .logger( f() )      -- command how function used to output traces is found
  .assert( cndtion )  -- bomb when condition is not met
  .de                 -- my de&&bug() darling
  .bug( ... )         -- alias for .trace()
  .mand( condition )  -- my de&&mand() darling, alias for .assert()

Table of Contents

Actor

  • ask() - Ask something to an actor, ie also expect a response.
  • become() - Change the way the actor handle the messages.
  • receive() - Define the new behavior of actor for the next message.
  • reply - Async response to some "ask type" message.
  • tell() - Send a message to this actor.

Aggregator

Call

  • apply() - Call a call, also resolve/reject its promise
  • call() - Call a call, also resolve/reject its promise.
  • promise - Call can have a promise attached to them.
  • signal - Alias for .apply()
  • then() - Calls are promises resolved/rejected when the call is called.

Campaign

Generator

  • close() -
  • get -
  • next() - Block task until generator yields a result.
  • promise - The "can next()" promise of a generator.
  • put -
  • then() - A generator is also a promise, resolved when the generator yields a result.
  • try_next() - Like .next() but never blocks
  • try_yield() - Like .yield() but never blocks.
  • yield() - Task produce a new result and wait for consumer.

Lock

MessageQueue

Mutex

  • close() - Close mutex, reject all pending promises.
  • promise - Mutexes queue promises that are resolved when mutex is entered.
  • release() - Release mutex, resolve next pending promise if any.
  • signal - Alias for Mutex.release()
  • then() - A mutex is a promise, resolved when mutex is entered.

Port

Promise

  • reject() - Reject a promise, with a reason.
  • resolve() - Resolve a promise, with a result.
  • signal - Alias for Promise##resolve()
  • then() - Attach callbacks to a promise.

ProxyActor

  • ask() - Send a message to a remote actor, also expect a response
  • defer() - Register callback to call when connection get lost
  • tell() - Send a message to a remote actor.
  • then() - Register callbacks to call when connection succeeds xor fails

Selector

  • promise - A selector has a promise attached to itself.
  • then() - A selector is also a promise.

Semaphore

  • close() - Close semaphore, reject pending promises.
  • promise - Make a new promise, queued, resolved when semaphore is released.
  • release() - Add resource to semaphore, may resolve next pending promise.
  • signal - Alias for Semaphore.release()

Signal

  • active - A signal is active when some entities expects its occurence.
  • close() - CLose signal, reject pending promise.
  • inactive - A signal is inactive when nothing is expecting its occurence.
  • promise - Returns an unresolved promise that next a_signal.signal() will resolve.
  • signal() - Signal the occurence of a signal.

Stage

  • defer() - Monitor loss of contact with a remote stage.
  • get() - Get stage local variable.
  • set() - Set stage local variable.

Task

  • aggregator = ProtoTask.all() - Make a promise that collects the outcome of sub promises
  • begin - l8.begin creates a sub task. The task is not scheduled until .end is
  • callback - A f( err, rslt ) function that will reject/resolve the task's promise.
  • canceled - task.canceled is true if task.cancel() was called.
  • current - return this
  • done - l8.done
  • end - .end schedule a sub task defined using .begin.
  • error - l8.error
  • fail - l8.fail
  • flow - l8.flow is similar to l8.walk but it detects errors. ie it is a node.js
  • generate() - Make a new generator.
  • id -
  • join() -
  • l8 - this.l8 is the root task in all code where "this" is bound to the current
  • label - l8.label return the value of the "label" task local variable. That value
  • lock() - l8#lock()
  • mutex() - A mutex is a binary semaphore.
  • name -
  • parent - l8.parent
  • parents - l8.parents
  • pause() - Pause execution of a task.
  • port() -
  • promise - Tasks are promises.
  • queue() -
  • raise() - Raise an error inside a task.
  • result - l8.result
  • resume() - Resume execution of paused task.
  • root - l8.root
  • selector() - L8#selector()
  • signal() - Alias for task.resume()
  • stopped - l8.stopped
  • stopping - l8.stopping
  • succeed - l8.succeed
  • tasks - l8.tasks
  • then() - Tasks are promises, resolved/rejected when tasks succeeds/fails.
  • timeout() - Make a new timeout that will bomb after a delay.
  • toString = ProtoTask.toLabel() - Task object have a .toString() and .toLabel() methods that provide a
  • trace -
  • wait() -
  • walk - l8.walk is used to pause/resume the current task when an async call is done.

Timeout

  • duration - How long since timeout was created or until it fired.
  • promise - Return the promise that is resolved when the timeout fires.
  • signal() - Fire timeout early.
  • signaled - Time when timeout was fired, or null
  • started - Time when the timeout was created. ie: l8.now at that time.
  • then() - Timeouts are promises resolved withing a fixed delay.

l8

  • Role() -
  • Task() - Build a "task constructor".
  • __defineGetter__() - As a convention, methods applied on the global root task l8 are forwared to
  • actor() - l8.actor()
  • and() - l8.and()
  • any() - l8.any()
  • assert() - l8.assert( cond ) raises an error when cond is satisfied.
  • binding() - Return the "binding" where a variable is stored.
  • break() - ToDo: l8.break( [val] )
  • bug() - l8.bug() is an alias for l8.trace() that enables the de&&bug() pattern.
  • call() - Create a function whose result will resolve a promise.
  • callback() - Register a node style callback called on promise's completion.
  • client() - True when running inside a browser. Opposite to l8.server.
  • clientize() - Act as if client. Override l8.client/l8.server auto-detection.
  • compile() - l8.compile() may need to be provided a well scoped "eval()" or else it's
  • compileGenerator() - l8.compileGenerator( fn )
  • compiler() - l8.compiler( fn )
  • continue() - ToDo: l8.continue( [val] )
  • countdown() - Exit process with error status 1 after a while.
  • dateNow() - l8.dateNow is the Date object sampled when l8.now was last sampled.
  • debug() - DEBUG mode currently defaults to "on". Please use l8.debug() to change it
  • defer() - l8.defer( fn ) pushes a function to execute when the current task is about
  • failure() - Some special errors are used to build control structures.
  • final() - l8.final( fn )
  • fork() - Queue a step that starts a forked task. forks "join".
  • http_port() - To expose the local actors to the outside world, an http server is required.
  • inspect() - l8.inspect( obj ) returns a string representation for the specified object
  • label() - l8.label = "xxx" sets the "label" thread local variable. That variable can
  • logger() - l8.logger( null ) restores the default logger for traces.
  • mand() - l8.mand( cond ) is an alias for l8.trace() that enable the de&&mand()
  • next() - Yield a new result for the active generator's consumer.
  • now() - l8.now provides a fast access to the time in millisec. That value is
  • parentTask() - Bootstrap root task, id 0
  • pause() - l8.paused
  • proceed() - l8.proceed( fn ) is used to pause/resume a task when dealing with async
  • promise() - Create a new promise.
  • promise_factory() - Use another promise factory, not l8's one.
  • proto() - l8.proto makes it easy for module to export a new mmm method by adding it
  • proxy() -
  • repeat() - l8.repeat( fn )
  • result() - l8.result = val assigns a new result to the task. If some step remains to
  • return() - l8.return( [val] )
  • server() - True when running inside node.js. Opposite to l8.client.
  • serverize() - Act as if server. Override l8.client/l8.server auto-detection.
  • set() - l8#get()
  • stage() - Monitor contact with a remote stage.
  • step() - Pause task for specified delay, milli-seconds.
  • stop() - l8.stop() set a flag for the task, asking it to stop gently.
  • success() - l8.success( fn )
  • task() - Queue a step in the task's step queue.
  • tick() - l8.tick( fn ) is a low level function that schedules the execution of a
  • trace() - User can redefine what logger l8.trace() uses, see l8.logger().
  • try_yield() - Alias for l8.try_yield()
  • var() - Create a "task local" variable.
  • wait() - Make a new semaphore.

Actor

actor.ask()

'Ask something to an actor, ie also expect a response.'

Usage:

an_actor.ask( "Do", "something" )
an_actor.ask( ["Do","something"], function( err, rslt ){...} )

Send an 'ask type' message to the actor. The actor should reply. Optional function( err, rslt ) is called with the outcome. If not provided, a promise is returned instead.

To reply, the actor can:

  1/ provide an immediate result, by returning it.
  2/ return a promise that it will fulfill later.
  3/ return nothing immediately and produce a result later. To do that,
  the actor is provided a reply callback that it must call with the outcome,
  ie either an error or a null error and a result. The callback that the actor
  must call is accessible using an_actor.reply. The actor for which the
  current task is running is accessible using this.ego. When the actor uses
  this method, ie when it accesses this.ego.reply, it means that the actor
  is ok to process another message. As a result, many requests can be
  processed in parallel instead of in sequence as it is the case when the
  actor returns a promise. Note: the actor can store the reply callback, it
  then can call it later, to provide an answer, maybe after it has processed
  some other messages.

actor.become()

'Change the way the actor handle the messages.'

Usage:

this.ego.become( function )
this.ego.become( behavior )

The way an actor processes the messages is initialy defined when the actor is first created. However, the actor can change this at any time.

This can be usefull for actors that accept or queue different messages depending on their internal state. When the state change, the actor can change behavior.

See also Actor#receive() when an actor needs to change the way it handles just one message.

actor.receive()

'Define the new behavior of actor for the next message.'

Usage:

this.ego.receive( function )
this.ego.receive( behavior, options )

When an unexpected message is received, it gets queued. Whenever the actor attempts to receive a new message, these queued messages are proposed first. Such queued messages are a backlog and it is the programmer's responsability to make sure that the backlog does not grow much or else performances will suffer.

The actor is normally idle, unless a timeout was specified (option). If some revious message was left unprocessed, the actor will process them if it wants. Or else, the actor waits for a new message to come in. When the special timeout value 0 is specified, only old messages are processed.

option { timeout: xx, after: function() } specifies what function to call if no message is received within the specified delay.

When the actor behavior depends on the reception of a single specific message, once that message is received, the actor gets back to its previous behavior. To change the behavior for more than one message, see Actor#become.

actor.reply

'Async response to some "ask type" message.'

Usage:

var reply = this.ego.reply
...
reply( err, rslt )

By default, actors process messages in sequence. However, if an actor wants to process messages in parallel, it can store a reply callback and reply later. When the actor gets the reply callback, l8 detects that and unlock the mailbox of the actor.

actor.tell()

'Send a message to this actor.'

Usage:

an_actor.tell( "Seen", "something" )
an_actor.tell( ["Seen", "something"], function( err ){...} )

Optional function( err ) is a callback. If it is not provided, a promise is returned instead. The only possible error is when an attempt is made to send a message to a dead/done actor or to a remote actor when the contact is lost.

Aggregator

aggregator.promise

aggregator.then()

'An aggregator is a promise too.'

Call

call.apply()

'Call a call, also resolve/reject its promise'

call.call()

'Call a call, also resolve/reject its promise.'

call.promise

'Call can have a promise attached to them.'

call.signal

'Alias for .apply()'

When a call object is signaled, the call's function is called.

call.then()

'Calls are promises resolved/rejected when the call is called.'

If the calls raises an exception, the promise is rejected. Or else, the the promise is resolved using the result of the call.

Campaign

campaign.deregister()

campaign.lookup()

campaign.register()

Generator

generator.close()

generator.get

generator.next()

'Block task until generator yields a result.'

Optional parameter is sent to the generator. It gets it as the result of the step that called .yield().

generator.promise

'The "can next()" promise of a generator.'

The default promise of a generator is the promise resolved when the consumer can get a new result without blocking. See also Generator.set about the promise resolved when the generator can yield a new result without being blocked.

generator.put

generator.then()

'A generator is also a promise, resolved when the generator yields a result.'

After the generator yields a result and resolve its promise, a new promise becomes pending.

generator.try_next()

'Like .next() but never blocks'

generator.try_yield()

'Like .yield() but never blocks.'

Returns [false] or [true,message]

generator.yield()

'Task produce a new result and wait for consumer.'

The value returned is what the consumer specified when it called .next(), or the undefined value if it specified nothing.

Lock

lock.close()

lock.promise

lock.release()

lock.signal

lock.task

lock.then()

MessageQueue

messagequeue.close()

messagequeue.empty

messagequeue.full

messagequeue.get()

messagequeue.in

messagequeue.out

messagequeue.promise

messagequeue.put()

messagequeue.signal

messagequeue.then()

Signals are promises that detect events. Repeatedly.

When a signal is signaled, it's current promise is resolved and a new promise is attached to it when a_signal.then() is called again. As a result, a_signal.then() is guarantee to succeed only when the next a_signal.signal() occurs. Note: when an unresolved promise is attached to a signal, the signal is active. After the signal is signaled, it becomes inactive, until it is activated again.

messagequeue.try_get()

messagequeue.try_put()

Mutex

mutex.close()

'Close mutex, reject all pending promises.'

mutex.promise

'Mutexes queue promises that are resolved when mutex is entered.'

Note: accessing .promise twice results in two more promises in the promise of of the mutex. I.e. make there that each promise, when resolved, eventually releases the mutex, or else queued promises will never be resolved. If the same task tries to reacquire a mutex, an exception is raised. That is not the case with re-entrant mutexe, See also Lock.promise

mutex.release()

'Release mutex, resolve next pending promise if any.'

mutex.signal

'Alias for Mutex.release()'

mutex.then()

'A mutex is a promise, resolved when mutex is entered.'

Duck typing so that Task.wait() works.

Port

port.get()

port.in

port.out

port.promise

port.put()

port.signal

port.then()

port.try_get()

'Like .get() but non blocking'

port.try_put()

'Like .put() but non blocking'

Promise

promise.reject()

'Reject a promise, with a reason.'

promise.resolve()

'Resolve a promise, with a result.'

promise.signal

'Alias for Promise##resolve()'

promise.then()

'Attach callbacks to a promise.'

Usage:

 a_promise.then(
   function( rslt ){ ... },
   function( err  ){ ... }
}

See http://promises-aplus.github.com/promises-spec/

The returned value is a new promise. That promise will be resolved with the result of the attached callbacks. If that result is an exception, the new promise is rejected, the reason is the exception.

ProxyActor

proxyactor.ask()

'Send a message to a remote actor, also expect a response'

Usage:

actor.ask( "Some", "question" ).then( function( r ){ console.log( r ) } )

If the contact is lost or if the remote actor replied with an error, the promise is rejected.

proxyactor.defer()

'Register callback to call when connection get lost'

proxyactor.tell()

'Send a message to a remote actor.'

Usage:

var promise = remote_actor.tell( "See", "you" )
promise.then( null, function( ko ){ console.log( "Contact was lost" ) } )

proxyactor.then()

'Register callbacks to call when connection succeeds xor fails'

Selector

selector.promise

'A selector has a promise attached to itself.'

selector.then()

'A selector is also a promise.'

Semaphore

semaphore.close()

'Close semaphore, reject pending promises.'

semaphore.promise

'Make a new promise, queued, resolved when semaphore is released.'

semaphore.release()

'Add resource to semaphore, may resolve next pending promise.'

semaphore.signal

'Alias for Semaphore.release()'

Signal

signal.active

'A signal is active when some entities expects its occurence.'

I.e. a signal is active when there is a pending promise. When an active signal is signaled, the promise is resolved.

signal.close()

'CLose signal, reject pending promise.'

signal.inactive

'A signal is inactive when nothing is expecting its occurence.'

An event occurence signaled on a inactive signal object is ignored.

signal.promise

'Returns an unresolved promise that next a_signal.signal() will resolve.'

Returns an already rejected promise if signal was closed.

signal.signal()

'Signal the occurence of a signal.'

Tasks that block on the signal are deblocked. Also resolves the promise attached to the signal. Signals are not buffered, the first signaled value is the value of the current promise. If another signal occurs before another promise is issued, it get lost. Ie, signaling an inactive signal is a noop. A new promise is issued when a_signal.then() is called, if the current promise is already resolved.

Stage

stage.defer()

'Monitor loss of contact with a remote stage.'

Usage:

this.ego.stage.defer( function(){ "client disconnected" } )

Note: multiple calls to .defer() will result in multiple functions being called when the contact with that stage gets lost.

stage.get()

'Get stage local variable.'

stage.set()

'Set stage local variable.'

Task

task.aggregator = ProtoTask.all()

'Make a promise that collects the outcome of sub promises'

The promise is never rejected. Instead, its value is an array of "results" where each result is [err,rslt] for the corresponding promise.

task.begin

'l8.begin creates a sub task. The task is not scheduled until .end is'

mentionned. Nested tasks are possible. .begin is useful to handle errors like javascript does with try/catch. Usage: l8.begin.step(...).step(...).end

task.callback

'A f( err, rslt ) function that will reject/resolve the task's promise.'

task.canceled

'task.canceled is true if task.cancel() was called.'

task.current

' return this'

task.done

'l8.done'

task.done is true when the task is done, ie terminated.

task.end

'.end schedule a sub task defined using .begin.'

Returns parent to make chaining possible: t.begin.step().step().end.step()

task.error

'l8.error'

task.error is the last error for the task. Null if none.

task.fail

'l8.fail'

task.fail is true when a task is done but failed, due to some error.

task.flow

'l8.flow is similar to l8.walk but it detects errors. ie it is a node.js'

friendly "walk" that checks first result to detect errors and throw error when present, or else filters out first result to set result of step using rest.

task.generate()

'Make a new generator.'

task.id

task.join()

task.l8

'this.l8 is the root task in all code where "this" is bound to the current'

task.

task.label

'l8.label return the value of the "label" task local variable. That value'

can be useful to output traces.

task.lock()

'l8#lock()'

A create a new lock, a re-entrant mutex

task.mutex()

'A mutex is a binary semaphore.'

See http://en.wikipedia.org/wiki/Mutual_exclusion

task.name

task.parent

'l8.parent'

task.parent is the parent task to the specified task.

task.parents

'l8.parents'

task.parents is an array of tasks starting from the specified task and ending with the top root task, with all the intermediary tasks in between.

task.pause()

'Pause execution of a task.'

Task will resume and execute next step when resume() is called.

task.port()

task.promise

'Tasks are promises.'

task.queue()

task.raise()

'Raise an error inside a task.'

Note: val parameter is needed when err is l8.returnEvent

task.result

'l8.result'

task.result is the result of a task. If the task is not finished, it is the last result produced by a step in that task.

task.resume()

'Resume execution of paused task.'

Execution restarts at step next to the one where the task was paused.

When provided a value, that value will be the input for the next step. See also l8.paused.

task.root

'l8.root'

task.root is, among task.parents, the task just below the topmost l8 task.

task.selector()

'L8#selector()'

Make a selector promise, resolved when one of many promises resolves.

task.signal()

'Alias for task.resume()'

l8#signal() Make a new signal. Signals are activable event detectors. Signals can be signaled multiple times. When a signal is signaled, the promise attached to it is resolved and a new promise can be attached to it (that will be resolved when the signal is signaled again). Note: if the signal occurs while nobody cares, it get lost, ie signals are event detectors, when inactive they detect nothing. Note: a new promise is attached to a signal using .promise or .then() ; this "clears" the previously signaled signal and reactivate the signal, ie it restarts the event detection.

task.stopped

'l8.stopped'

task.stopped is true if a task is done and was asked to terminate using task.stop

task.stopping

'l8.stopping'

task.stopping is true if task is not terminated but was asked to terminate because task.stop() was called.

task.succeed

'l8.succeed'

task.succeed is true when the task is done and did not fail.

task.tasks

'l8.tasks'

task.tasks returns an array with one item for each direct sub task of the specified task.

task.then()

'Tasks are promises, resolved/rejected when tasks succeeds/fails.'

task.timeout()

'Make a new timeout that will bomb after a delay.'

task.toString = ProtoTask.toLabel()

'Task object have a .toString() and .toLabel() methods that provide a'

value that can be useful to output trace messages when debugging. See also l8.label to get/set the user label associated to a task.

task.trace

task.wait()

task.walk

'l8.walk is used to pause/resume the current task when an async call is done.'

It provides a callback that will use its parameter to set the output of the step, an output that will become the input of the next step.

Timeout

timeout.duration

'How long since timeout was created or until it fired.'

timeout.promise

'Return the promise that is resolved when the timeout fires.'

timeout.signal()

'Fire timeout early.'

Resolves the promise of the timeout early.

timeout.signaled

'Time when timeout was fired, or null'

timeout.started

'Time when the timeout was created. ie: l8.now at that time.'

timeout.then()

'Timeouts are promises resolved withing a fixed delay.'

It is possible to fire them early, using to.signal().

l8

l8.Role()

l8.Task()

'Build a "task constructor". '

Usage:

var do_it = l8.Task( function( p1, p2, ... ){...} }
do_it( "Just", "the maximum" )

A task constructor is, at the step level, the equivalent of a "function" at the javascript statement level. I.e. calling a task constructor and calling a function are similar actions.

When a task constructor is called, it creates a task with the supplied parameters and returns a promise that will be resolved or rejected depending on the task outcome. The task that calls the task constructor waits for that promise to be fulfilled.

Return a "Generator Constructor". This function behaves much like l8.Task() does but the returned value is a Generator Task, not just a regular Task. I.e. it can "yield".

l8.()

'As a convention, methods applied on the global root task l8 are forwared to'

the "current task". For normal task, .current is simply an attribute whose value never changes and is a reference to the object itself, ie obj.current == obj

l8.actor()

'l8.actor()'

Look up for an actor or create one (or create an actor generator). Usage:

l8.actor( name )      -- look for a local actor or a known remote actor
l8.actor( name, url ) -- return a proxy to access a remote actor
l8.actor( name, function ) -- create an actor, managed by a function
l8.actor( name, behavior ) -- idem with an initial behavior

When the name ends with a dot, it is an actor generator that is returned. When that generator is called, a new actor is created, whose name is the actor generator name plus a sequential number.

Actors behaviors are described in Actor#receive()

l8#actor

Usage:

var remote_actor = l8.actor( "service", "http://some.server.com" )

Remote actors and local actors both provide a .tell() and .ask() methods so that for most usage, they are identical. See also ProxyActor#then() and ProxyActor#defer() about contact monitoring because things are sligthly different when the actor is remote.

l8.actor() Look up for an existing proxied actor.

Usage:

var actor = l8.actor( "service" )

If an actor with the specified name was referenced before, it is that object which is returned. Or else, null is returned.

l8.and()

'l8.and()'

Make a promise that depends on sub promises.

l8.any()

'l8.any()'

Alias for l8.selector() See also l8.or()

L8#or() Make a promise resolved when one of many promises resolves with thruth. If one of the sub promises is rejected, the whole promise is rejected. See also l8.any()

l8.assert()

'l8.assert( cond ) raises an error when cond is satisfied.'

l8.binding()

'Return the "binding" where a variable is stored.'

Usage:

var vars = l8.binding();
var task = vars.task;
for( name in vars ){
  if( name === "task" )continue;
  if( vars[name].task ){
    console.log( "task " + task + " accesses " + name
    + " defined by parent task " + vars[name].task );
  }else{
    console.log( "task " + task + " defines " + name );
  }
}

Usage:

 var manager = l8.binding( "session" ).task

A binding is an object with a "task" property (the binding owner) and a property for each variable ever accessed by that task or it's sub tasks. Such properties have a "value" property when that variable is stored directly inside that binding. Or else they have a "task" property that tells which task actually stores the variable's value.

See also l8.var().

l8.break()

'ToDo: l8.break( [val] )'

l8.break task.break raises a l8.breakEvent in the specified task. That exception is propagated to the parent tasks, up to a task where l8.repeat() is active. That loop is then terminated.

l8.bug()

'l8.bug() is an alias for l8.trace() that enables the de&&bug() pattern.'

l8.call()

'Create a function whose result will resolve a promise.'

The first parameter is a function. The second parameter is what "this" will be bound to when that function is called/signaled, it is optional.

The returned function is also a promise, ie it implements .then(). The promise is resolved/rejected when the function is called, using its result.

Note: this promise resolution is invisible to the entity that calls the function, the result xor exception are returned as if the function was a regular function, called synchronously (the promise callbacks are called later, async).

Usage:

var f = l8.call( function( msg ){ return this.handle( msg ) }, handler )
f.then( function( r ){ console.log( r,  "world!" ); } )
...
console.log( f( "Hello" ) );

Usage:

l8.step( function(){
  var read
  fs.readFile( "xx", "utf8", read = l8.call( function( err, rslt ){
    if( err ) throw err;
    return rslt
  }
  return read
}).step( function( r ){ console.log( "content of xx: " + r ); })

When the pending call is eventually called, the promise attached to it is resolved/rejected, depending on the outcome of the call.

l8.callback()

'Register a node style callback called on promise's completion.'

Usage:

 l8.callback( a_promise, function( err, rslt ){ ... } )

Usage:

  a_task.callback( function( err, rslt ){ ... } )
Promise defaults to current task when not specified.

In both cases, the returned value is a promise. See also Promise#then()

l8.client()

'True when running inside a browser. Opposite to l8.server.'

l8.clientize()

'Act as if client. Override l8.client/l8.server auto-detection. '

l8.compile()

'l8.compile() may need to be provided a well scoped "eval()" or else it's'

result function may lack access to the global variables referenced by the code to (re)compile. This should be necessary on nodejs only, not in browsers

l8.compileGenerator()

'l8.compileGenerator( fn )'

This method compiles a generator constructor using the source of the specified "fn" function

l8.compiler()

'l8.compiler( fn )'

Expand some macros to make a "task constructor" or a "generator constructor".

l8.continue()

'ToDo: l8.continue( [val] )'

l8.continue task.continue raises a l8.continueEvent in the task. That exception is propagated to the parent tasks, up to a task where l8.repeat() is active. That loop is then reentered.

l8.countdown()

'Exit process with error status 1 after a while.'

Displays a stressfull message every second until that.

l8.dateNow()

'l8.dateNow is the Date object sampled when l8.now was last sampled.'

l8.debug()

'DEBUG mode currently defaults to "on". Please use l8.debug() to change it'

l8.debug() returns true of false depending on the debug flag setting. l8.debug( v ) sets that flag to true or false.

l8.de&&bug( ... ), where "l8.de" changes according to l8.debug()

l8.defer()

'l8.defer( fn ) pushes a function to execute when the current task is about'

to terminate. These functions are executed in LIFO order. See documentation about the difference between .defer() and .final()

l8.failure()

'Some special errors are used to build control structures.'

l8.cancelEvent is the value of the parameter for the l8.failure() specified function when the failure is due to a cancellation.

task.cancel() terminates the specified task and its sub tasks. When a task get canceled, a l8.cancelEvent exception is raised. That error can be captured with l8.failure() and l8.final(). After a task.cancel(), task.canceled is true.

l8.failure( fn ) task.failure() specifies a function to be called when the task terminates. The function receives a paremeter, l8.error, the last error. The function is called only if the task is failing. It is called before the function specified using l8.final()

l8.final()

'l8.final( fn )'

task.final() specifies a function to be called when the task terminates. The function receives two paremeters. The first one is l8.error, the last error, the second one is l8.result, the last result.

l8.fork()

'Queue a step that starts a forked task. forks "join".'

l8.fork() is similar to l8.task() but makes it possible to run multiple sub tasks in parallel. The result of each forked task is accumulated into an array, respecting the forked tasks creation order. That array becomes the input of the next step.

Usage:

l8.fork( function(      ){  return "a";
}).fork( function(      ){  return "b";
}).step( function( a, b ){  return a + b;
}).then( function( ab   ){  console.log( ab ); })

See also l8.spawn().

l8.http_port()

'To expose the local actors to the outside world, an http server is required.'

If none is provided, one will be created, using the specified port or a reasonnable default one that depends on the running platform.

l8.inspect()

'l8.inspect( obj ) returns a string representation for the specified object'

that can be useful to output traces.

l8.label()

'l8.label = "xxx" sets the "label" thread local variable. That variable can'

be useful in trace messages.

l8.logger()

'l8.logger( null ) restores the default logger for traces.'

This is the function that returns the default log() function that l8.trace() uses. That returned function must have the signature of console.log()

l8.mand()

'l8.mand( cond ) is an alias for l8.trace() that enable the de&&mand()'

pattern for asserts that can be disabled with very little overhead left.

l8.next()

'Yield a new result for the active generator's consumer.'

See also Generator.yield() when in need of nested generator. See also l8.next() about how to receive results.

l8.next() Consume another result from the active peer generator. See also Generator.next() when in need of nested generators.

l8.now()

'l8.now provides a fast access to the time in millisec. That value is'

sample whenever the l8 scheduler wakes up. As a result, it can be usefull to correlate sub events that pertain to the same evant that woke up the scheduler.

l8.parentTask()

'Bootstrap root task, id 0'

l8.pause()

'l8.paused'

task.paused is true if the specified task is paused. See l8.pause() & l8.resume()

l8.proceed()

'l8.proceed( fn ) is used to pause/resume a task when dealing with async'

functions. The fn function is called with the parameters provided by the async function. The returned value of that fn function, unless undefined, provide the output for the step, an output that will be input for the next step. See also l8.walk and l8.flow that are simpler to use in most cases. Usage:

 l8.step( function(){ fs.readFile( "xx", "utf8",
   l8.proceed( function( err, content ){ return [err,content]; })

Pauses current task and returns a callback to be called to resume execution.

l8.promise()

'Create a new promise.'

See Promise#then()

a function( err, rslt ) that will resolve or reject the promise.

Usage:

var read = l8.promise();
fs.readFile( "xx", "utf8", read.callback );
read.then( function( content ){ console.log( "xx:" + content ); } );

l8.promise_factory()

'Use another promise factory, not l8's one.'

Usage:

 l8.promise_factory( require( "Q" ).defer );

l8.proto()

'l8.proto makes it easy for module to export a new mmm method by adding it'

to the prototype of all the task objects. As a result this.mmm() will be directly accessible in the code body of all tasks, ie where "this" is bound to the current task.

l8.proxy()

l8.repeat()

'l8.repeat( fn )'

Add a step that will repeately execute the specified function. Please use l8.break to exit that loop.

l8.result()

'l8.result = val assigns a new result to the task. If some step remains to'

be executed, that result will be the input for the step.

l8.return()

'l8.return( [val] )'

task.return() terminates the specified task. Sub tasks are left untouched. In the task, execution jumps to the deferred clause and .final() clause.

l8.server()

'True when running inside node.js. Opposite to l8.client.'

Note: detection is currently based on success of require( "util" ).

l8.serverize()

'Act as if server. Override l8.client/l8.server auto-detection. '

l8.set()

'l8#get()'

Get the value of a task's variable. If the current task does not define that variable in it's own binding, follow binding chain in parent task. See also l8.set()

l8.stage()

'Monitor contact with a remote stage.'

Usage:

 var stage = l8.stage( "http//some.server.com" ).then(
   function( ok ){ remote_actor = l8.actor( "service", stage ),
   function( ko ){ console.log( "Cannot contact server", ko ) }
 )

A stage is a promise that is resolved when the contact is established or is rejected if the contact cannot be established. To handle the loss of an established contact, see Stage#defer()

l8.stage() Set up contact with a remote stage.

Usage:

var stage = l8.stage( "http://some.server.com" )
var actor = l8.actor( "service", stage )

l8.step()

'Pause task for specified delay, milli-seconds.'

Usage:

l8.step( function(){ l8.sleep( 1000 ); } );

Semaphores are queued promises, resolved when semaphore is released. Usage:

 l8.step(  function(){  l8.wait( a_semaphore );
 )}.step(  function(){  console.log( "resource acquired" );
 }).step(  function(){  ...
 }).final( function(){  a_semaphore.release(); })

Usage:

 a_semaphore.then( function(){ console.log( "resource acquired" ); } );

See also Promise.then()

l8#select() Block task until one of many promises delivers.

Usage:

l8.step( function(){
  var timeout = l8.timeout( 1000 );
  var read
  fs.readFile( "xx", "utf8", read = l8.call( function( err, rslt ){
    return [err,rslt]
  });
  l8.select( read, timeout )
}).step( function( err, rslt ){ if( err ){ ... }else{ ...use rslt... } )}

l8.stop()

'l8.stop() set a flag for the task, asking it to stop gently.'

See also l8.cancel() for a more brutal way to stop a task.

l8.success()

'l8.success( fn )'

task.success() specifies a function to be called when the task terminates. The function receives a paremeter, l8.result, the last result. The function is called only if the task is a success. It is called before the function specified using l8.final()

l8.task()

'Queue a step in the task's step queue.'

Usage:

l8.task( function(){
  l8.step( function(){
    console.log( "first step" );
    return ["hello", "world"];
  }).step( function( h, w ){
    console.log( h, w )
  })
})

The output, ie result, of a step is the input, ie parameters, of the next one. Steps can block, using l8.pause() && l8.resume().

When the output of a step is a promise, the step is blocked until the promise is resolved. The promise's value becomes the new value for the output of the step.

l8.task(), l8.fork() and l8.repeat() queue "special" steps in the current task.

The new step is inserted in the task step queue. It is inserted after the current step for that task. If multiple steps are inserted, they are inserted in FIFO order, ie the last inserted step will be executed last.

Note: l8.step(...) inserts a step in the "current" task, not the l8 task. The l8 task is the root task, it is the ultimate "parent" task of all tasks. The returned value of the .step() function is the actual task whose queue was augmented.

Queue a step that calls a task.

Usage:

l8.task( function(){
  fs.readFile( "xx.txt", "utf8", l8.flow );
  this.step( function( c ){ return c.toLowerCase() }
}).then( function( r ){ console.log( "lower cased xx.txt: ", r ) })

a_task.task() adds a step that will start a new task with some initial step to execute. The step is blocked until the sub task is done.

If there is no current task, ie if the current task is the l8 root task, the new task is created immediatly and it is the return value of the function. If there is a current task, the returned value is that task object. The new task object is created just before the task starts executing it's first step.

Usage:

var new_task = null
l8.task( function(){
  task = this;
  ...
})

See also l8.fork() and l8.spawn().

l8#spawn() Queue a step that will start a "detached" sub task. Contrary to tasks created using l8.task() and l8.fork() (or using a task constructor created using l8.Task()), "detached" tasks don't block their parent task.

See also l8.task()

l8.tick()

'l8.tick( fn ) is a low level function that schedules the execution of a'

function by the javascript event loop. It is the equivalent to setTimeout() with a 0 delay.

l8.trace()

'User can redefine what logger l8.trace() uses, see l8.logger().'

Change the way l8.trace() outputs messages.

Usage:

var save = l8.logger()
 l8.logger( function(){ return function(){
   console.log.apply( console, arguments )
 })

Parameter "get_logger" is a function returning a logger, ie a function called to log messages, like console.log(). l8.trace() will use it. To restore it to its default value, use l8.logger( null ). To read the current value, use l8.logger() Usage: l8.logger( function(){ return console.log } } Note: because "get_logger" is a function that returns the logger function, ie because of this added level of indirection, user can fine tune logging using information available in the context of the application when the logger function is necessary, versus the information available when the logger is installed.

l8.try_yield()

'Alias for l8.try_yield()'

l8.var()

'Create a "task local" variable.'

Usage:

 l8.var( "session", session );

The variable is visible by the current task's sub tasks. See also l8.get() and l8.set().

Note: when a task is done, it's bindings are erased. As a consequence, any pending spawn task gets inherited by the done task's parent task and cannot access the erased bindings they previously accessed, resulting in access attemtps returning typically "undefined" instead of the expected value. To avoid such a situation, when spawn tasks access shared variables from their parent, please make sure that the parent task does not terminate until all spawn tasks are done first. See also l8.join().

Note: please use l8.global() to create variables in the root binding because l8.var() will actually create a variable in the current task when applied on the root l8 task.

l8#global() Create a global "task local variable" that all tasks share. See also l8.var()

l8#set() Assign a new value to a task local variable. Change the value of an existing task local variable or create a new variable as l8.var() would do. See also l8.get()

l8.wait()

'Make a new semaphore.'

See http://en.wikipedia.org/wiki/Semaphore_(programming) P is .signal() or .release(). V is l8.wait( a_semaphore ) or a_semaphore.then(...)

Clone this wiki locally