, (*1)
Reference implementation of:, (*2)
FTN12: FutoIn Async API
Version: 1.7
Spec: FTN12: FutoIn Async API v1.x, (*3)
Web Site, (*4)
About
Adds classical linear program flow structure to async programming
supporting exceptions. error handlers, timeouts, unlimited number of sub-steps,
execution parallelism and job state/context variables., (*5)
There is little benefit of using this tool in classical PHP aproach of one-request-one-execution approach.
However, it is base for FutoIn Invoker and Executor implementation, which target at daemon-like
execution pattern., (*6)
It should be possible to use any other async framework from AsyncSteps by using
setCancel() and/or setTimeout() methods which allow step completion without success() or
error() result. Specific step-associated AsyncSteps interface will be valid for success() or error()
call on external event., (*7)
It also possible to use FutoIn AsyncSteps from any other event framework. Just make sure to notify
external framework from the top most error handler (for errors) and last step (for success)., (*8)
To minimize cost of closure creation on repetitive execution, a special feature of "model" step
is available: model step is created as usual, but must never get executed. It possible to copy steps
and state variables using AsyncSteps#copyFrom() to a newly created object., (*9)
Installation
Command line:, (*10)
$ composer require 'futoin/core-php-ri-asyncsteps'
and/or composer.json:, (*11)
"require" : {
"futoin/core-php-ri-asyncsteps" : "^1.1",
}
Concept
NOTE: copy&paste from FTN12: FutoIn Async API v1.x, (*12)
This interface was born as a secondary option for
executor concept. However, it quickly became clear that
async/reactor/proactor/light threads/etc. should be base
for scalable high performance server implementations, even though it is more difficult for understanding and/or debugging.
Traditional synchronous program flow becomes an addon
on top of asynchronous base for legacy code and/or too
complex logic., (*13)
Program flow is split into non-blocking execution steps, represented
with execution callback function. Processing Unit (eg. CPU) halting/
spinning/switching-to-another-task is seen as a blocking action in program flow., (*14)
Any step must not call any of blocking functions, except for synchronization
with guaranteed minimal period of lock acquisition.
Note: under minimal period, it is assumed that any acquired lock is
immediately released after action with O(1) complexity and no delay
caused by programmatic suspension/locking of executing task, (*15)
Every step is executed sequentially. Success result of any step
becomes input for the following step., (*16)
Each step can have own error handler. Error handler is called, if
AsyncSteps.error() is called within step execution or any of its
sub-steps. Typical behavior is to ignore error and continue or
to make cleanup actions and complete job with error., (*17)
Each step can have own sequence of sub-steps. Sub-steps can be added
only during that step execution. Sub-step sequence is executed after
current step execution is finished., (*18)
If there are any sub-steps added then current step must not call
AsyncSteps.success() or AsyncSteps.error(). Otherwise, InternalError
is raised., (*19)
It is possible to create a special "parallel" sub-step and add
independent sub-steps to it. Execution of each parallel sub-step
is started all together. Parallel step completes with success
when all sub-steps complete with success. If error is raised in
any sub-step of parallel step then all other sub-steps are canceled., (*20)
Out-of-order cancel of execution can occur by timeout,
execution control engine decision (e.g. Invoker disconnect) or
failure of sibling parallel step. Each step can install custom
on-cancel handler to free resources and/or cancel external jobs.
After cancel, it must be safe to destroy AsyncSteps object., (*21)
AsyncSteps must be used in Executor request processing. The same
[root] AsyncSteps object must be used for all asynchronous tasks within
given request processing., (*22)
AsyncSteps may be used by Invoker implementation., (*23)
AsyncSteps must support derived classes in implementation-defined way.
Typical use case: functionality extension (e.g. request processing API)., (*24)
For performance reasons, it is not economical to initialize AsyncSteps
with business logic every time. Every implementation must support
platform-specific AsyncSteps cloning/duplicating., (*25)
1.1. Levels
When AsyncSteps (or derived) object is created all steps are added
sequentially in Level 0 through add() and/or parallel(). Note: each
parallel() is seen as a step., (*26)
After AsyncSteps execution is initiated, each step of Level 0 is executed.
All sub-steps are added in Level n+1. Example:, (*27)
add() -> Level 0 #1
add() -> Level 1 #1
add() -> Level 2 #1
parallel() -> Level 2 #2
add() -> Level 2 #3
parallel() -> Level 1 #2
add() -> Level 1 #3
parallel() -> Level 0 #2
add() -> Level 0 #3
Execution cannot continue to the next step of current Level until all steps of higher Level
are executed., (*28)
The execution sequence would be:, (*29)
Level 0 add #1
Level 1 add #1
Level 2 add #1
Level 2 parallel #2
Level 2 add #3
Level 1 parallel #2
Level 1 add #3
Level 0 parallel #2
Level 0 add #3
1.2. Error handling
Due to not linear programming, classic try/catch blocks are converted into execute/onerror.
Each added step may have custom error handler. If error handler is not specified then
control passed to lower Level error handler. If non is defined then execution is aborted., (*30)
Example:, (*31)
add( -> Level 0
func( as ){
print( "Level 0 func" )
add( -> Level 1
func( as ){
print( "Level 1 func" )
as.error( "myerror" )
},
onerror( as, error ){
print( "Level 1 onerror: " + error )
as.error( "newerror" )
}
)
},
onerror( as, error ){
print( "Level 0 onerror: " + error )
as.success( "Prm" )
}
)
add( -> Level 0
func( as, param ){
print( "Level 0 func2: " + param )
as.success()
}
)
Output would be:, (*32)
Level 0 func
Level 1 func
Level 1 onerror: myerror
Level 0 onerror: newerror
Level 0 func2: Prm
In synchronous way, it would look like:, (*33)
variable = null
try
{
print( "Level 0 func" )
try
{
print( "Level 1 func" )
throw "myerror"
}
catch ( error )
{
print( "Level 1 onerror: " + error )
throw "newerror"
}
}
catch( error )
{
print( "Level 0 onerror: " + error )
variable = "Prm"
}
print( "Level 0 func2: " + variable )
1.3. Wait for external resources
Very often, execution of step cannot continue without waiting for external event like input from network or disk.
It is forbidden to block execution in event waiting. As a solution, there are special setTimeout() and setCancel()
methods., (*34)
Example:, (*35)
add(
func( as ){
socket.read( function( data ){
as.success( data )
} )
as.setCancel( function(){
socket.cancel_read()
} )
as.setTimeout( 30_000 ) // 30 seconds
},
onerror( as, error ){
if ( error == timeout ) {
print( "Timeout" )
}
else
{
print( "Read Error" )
}
}
)
1.4. Parallel execution abort
Definition of parallel steps makes no sense to continue execution if any of steps fails. To avoid
excessive time and resources spent on other steps, there is a concept of canceling execution similar to
timeout above., (*36)
Example:, (*37)
as.parallel()
.add(
func( as ){
as.setCancel( function(){ ... } )
// do parallel job #1
as.state()->result1 = ...;
}
)
.add(
func( as ){
as.setCancel( function(){ ... } )
// do parallel job #1
as.state()->result2 = ...;
}
)
.add(
func( as ){
as.error( "Some Error" )
}
)
as.add(
func( as ){
print( as.state()->result1 + as.state->result2 )
as.success()
}
)
1.5. AsyncSteps cloning
In long living applications the same business logic may be re-used multiple times
during execution., (*38)
In a REST API server example, complex business logic can be defined only once and
stored in a kind of AsyncSteps object repository.
On each request, a reference object from the repository would be copied for actual
processing with minimal overhead., (*39)
However, there would be no performance difference in sub-step definition unless
its callback function is also created at initialization time, but not at parent
step execution time (the default concept). So, it should be possible to predefine
those as well and copy/inherit during step execution. Copying steps must also
involve copying of state variables., (*40)
Example:, (*41)
AsyncSteps req_repo_common;
req_repo_common.add(func( as ){
as.add( func( as ){ ... } );
as.copyFrom( as.state().business_logic );
as.add( func( as ){ ... } );
});
AsyncSteps req_repo_buslog1;
req_repo_buslog1
.add(func( as ){ ... })
.add(func( as ){ ... });
AsyncSteps actual_exec = copy req_repo_common;
actual_exec.state().business_logic = req_repo_buslog1;
actual_exec.execute();
However, this approach only make sense for deep performance optimizations., (*42)
1.6. Implicit as.success()
If there are no sub-steps added, no timeout set and no cancel handler set then
implicit as.success() call is assumed to simplify code and increase efficiency., (*43)
as.add(func( as ){
doSomeStuff( as );
})
1.7. Error Info and Last Exception
Pre-defined state variables:, (*44)
-
error_info - value of the second parameter passed to the last as.error() call
-
last_exception - the last exception caught, if feasible
Error code is not always descriptive enough, especially, if it can be generated in multiple ways.
As a convention special "error_info" state field should hold descriptive information of the last error.
Therefore, as.error() is extended with optional parameter error_info., (*45)
"last_exception" state variables may hold the last exception object caught, if feasible
to implement. It should be populated with FutoIn errors as well., (*46)
1.8. Async Loops
Almost always, async program flow is not linear. Sometimes, loops are required., (*47)
Basic principals of async loops:, (*48)
as.loop( func( as ){
call_some_library( as );
as.add( func( as, result ){
if ( !result )
{
// exit loop
as.break();
}
} );
} )
Inner loops and identifiers:, (*49)
// start loop
as.loop(
func( as ){
as.loop( func( as ){
call_some_library( as );
as.add( func( as, result ){
if ( !result )
{
// exit loop
as.continue( "OUTER" );
}
as.success( result );
} );
} );
as.add( func( as, result ){
// use it somehow
as.success();
} );
},
"OUTER"
)
Loop n times., (*50)
as.repeat( 3, func( as, i ){
print( 'Iteration: ' + i )
} )
Traverse through list or map:, (*51)
as.forEach(
[ 'apple', 'banana' ],
func( as, k, v ){
print( k + " = " + v )
}
)
1.8.1. Termination
Normal loop termination is performed either by loop condition (e.g. as.forEach(), as.repeat())
or by as.break() call. Normal termination is seen as as.success() call., (*52)
Abnormal termination is possible through as.error(), including timeout, or external as.cancel().
Abnormal termination is seen as as.error() call., (*53)
1.9. The Safety Rules of libraries with AsyncSteps interface
- as.success() should be called only in top-most function of the
step (the one passed to as.add() directly)
- setCancel() and/or setTimeout() must be called only in top most function
as repeated call overrides in scope of step
1.10. Reserved keyword name clash
If any of API identifiers clashes with reserved word or has illegal symbols then
implementation-defined name mangling is allowed, but with the following guidelines
in priority., (*54)
Pre-defined alternative method names, if the default matches language-specific reserved keywords:, (*55)
-
loop -> makeLoop
-
forEach -> loopForEach
-
repeat -> repeatLoop
-
break -> breakLoop
-
continue -> continueLoop
- Otherwise, - try adding underscore to the end of the
identifier (e.g. do -> do_)
Examples
Simple steps
use \FutoIn\RI\AsyncSteps;
use \FutoIn\RI\ScopedSteps;
$root_as = new ScopedSteps();
$root_as->add(
function( $as ){
$as->success( "MyValue" );
}
)->add(
function( $as, $arg ){
if ( $arg === 'MyValue' )
{
$as->add( function( $as ){
$as->error( 'MyError', 'Something bad has happened' );
});
}
$as->successStep();
},
function( $as, $err )
{
if ( $err === 'MyError' )
{
$as->success( 'NotSoBad' );
}
}
);
$root_as->add(
function( $as, $arg )
{
if ( $arg === 'NotSoBad' )
{
echo 'MyError was ignored: ' . $as->state()->error_info . PHP_EOL;
}
$as->state()->p1arg = 'abc';
$as->state()->p2arg = 'xyz';
$p = $as->parallel();
$p->add( function( $as ){
echo 'Parallel Step 1' . PHP_EOL;
$as->add( function( $as ){
echo 'Parallel Step 1->1' . PHP_EOL;
$as->p1 = $as->p1arg . '1';
$as->success();
} );
} )
->add( function( $as ){
echo 'Parallel Step 2' . PHP_EOL;
$as->add( function( $as ){
echo 'Parallel Step 2->1' . PHP_EOL;
$as->p2 = $as->p2arg . '2';
$as->success();
} );
} );
}
)->add( function( $as ){
echo 'Parallel 1 result: ' . $as->state()->p1 . PHP_EOL;
echo 'Parallel 2 result: ' . $as->p2 . PHP_EOL;
} );
// Note: we use ScopedSteps instead of AsyncSteps in this example
//$root_as->execute();
$root_as->run();
Result:, (*56)
MyError was ignored: Something bad has happened
Parallel Step 1
Parallel Step 2
Parallel Step 1->1
Parallel Step 2->1
Parallel 1 result: abc1
Parallel 2 result: xyz2
External event wait
use \FutoIn\RI\AsyncSteps;
use \FutoIn\RI\ScopedSteps;
use \FutoIn\RI\AsyncTool;
function dummy_service_read( $success, $error ){
// We expect it calles success when data is available
// and error, if error occurs
// Returns some request handle
return null;
}
function dummy_service_cancel( $reqhandle ){
// We assume it cancels previously scheduled reqhandle
}
$root_as = new ScopedSteps();
$root_as->add( function( $as ){
AsyncTool::callLater( function() use ( $as ) {
$as->success( 'async success()' );
} );
$as->setTimeout( 10 ); // ms
} )->add(
function( $as, $arg ){
echo $arg . PHP_EOL;
$reqhandle = dummy_service_read(
function( $data ) use ( $as ) {
$as->success( $data );
},
function( $err ) use ( $as ) {
if ( $err !== 'SomeSpecificCancelCode' )
{
$as->error( $err );
}
}
);
$as->setCancel(function($as) use ( $reqhandle ) {
dummy_service_cancel( $reqhandle );
});
// OPTIONAL. Set timeout of 1s
$as->setTimeout( 1000 );
},
function( $as, $err )
{
echo $err . ": " . $as->error_info . PHP_EOL;
}
);
// Note: we use ScopedSteps instead of AsyncSteps in this example
//$root_as->execute();
$root_as->run();
Result:, (*57)
async success()
Timeout:
Model steps (avoid closure creation overhead on repetitive execution)
use \FutoIn\RI\AsyncSteps;
use \FutoIn\RI\AsyncToolTest;
// Note, we have no default event engine in PHP
AsyncToolTest::init();
$model_as = new AsyncSteps();
$model_as->state()->variable = 'Vanilla';
$model_as->add( function($as){
echo '-----' . PHP_EOL;
echo 'Hi! I am from model_as' . PHP_EOL;
echo 'State.var: ' . $as->variable . PHP_EOL;
$as->variable = 'Dirty';
$as->success();
});
for ( $i = 0; $i < 3; ++$i )
{
$root_as = new AsyncSteps();
$root_as->copyFrom( $model_as );
$root_as->add( function( $as ) use ( $model_as ) {
$as->add( function( $as ){
echo '>> The first inner step' . PHP_EOL;
$as->success();
});
$as->copyFrom( $model_as );
$as->successStep();
});
$root_as->execute();
}
// Process all pending events
AsyncToolTest::run();
Result. Please note the order as only the first step is executed in the loop.
The rest is executed quasi-parallel by nature of async programming.
The model_as closure gets executed 6 times, but created only once., (*58)
-----
Hi! I am from model_as
State.var: Vanilla
-----
Hi! I am from model_as
State.var: Vanilla
-----
Hi! I am from model_as
State.var: Vanilla
>> The first inner step
>> The first inner step
>> The first inner step
-----
Hi! I am from model_as
State.var: Dirty
-----
Hi! I am from model_as
State.var: Dirty
-----
Hi! I am from model_as
State.var: Dirty
API documentation
, (*59)
API Index
, (*60)
FutoIn\RI\AsyncSteps
AsyncSteps reference implementation as per "FTN12: FutoIn Async API", (*61)
- Class name: AsyncSteps
- Namespace: FutoIn\RI
- This class implements: FutoIn\AsyncSteps
Methods
__construct
mixed FutoIn\RI\AsyncSteps::__construct(object $state)
Init, (*62)
Arguments
- $state object - <p>for INTERNAL use only</p>
add
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::add(callable $func, callable $onerror)
Add \$func step executor to end of current AsyncSteps level queue, (*63)
Arguments
- $func callable - <p>void execute_callback( AsyncSteps as[, previous_success_args] )</p>
- $onerror callable - <p>OPTIONAL: void error_callback( AsyncSteps as, error )</p>
copyFrom
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::copyFrom(\FutoIn\RI\AsyncSteps $other)
Copy steps from other AsyncSteps, useful for sub-step cloning, (*64)
Arguments
parallel
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::parallel(callable $onerror)
Create special object to queue steps for execution in parallel, (*65)
Arguments
- $onerror callable - <p>OPTIONAL: void error_callback( AsyncSteps as, error )</p>
state
\StdClass FutoIn\RI\AsyncSteps::state()
Access AsyncSteps state object, (*66)
success
mixed FutoIn\RI\AsyncSteps::success()
Set "success" state of current step execution, (*67)
successStep
mixed FutoIn\RI\AsyncSteps::successStep()
Call success() or add efficient dummy step equal to as.success() in behavior
(depending on presence of other sub-steps), (*68)
error
mixed FutoIn\RI\AsyncSteps::error(string $name, string $error_info)
Set "error" state of current step execution, (*69)
Arguments
- $name string - <p>Type of error</p>
- $error_info string - <p>Error description to be put into "error_info" state field</p>
setTimeout
mixed FutoIn\RI\AsyncSteps::setTimeout(integer $timeout_ms)
Delay further execution until as.success() or as.error() is called, (*70)
Arguments
- $timeout_ms integer - <p>Timeout in milliseconds</p>
__invoke
mixed FutoIn\RI\AsyncSteps::__invoke()
PHP-specific alias for success(), (*71)
setCancel
mixed FutoIn\RI\AsyncSteps::setCancel($oncancel)
Set cancellation callback, (*72)
Arguments
- $oncancel mixed - <p>void cancel_callback( AsyncSteps as )
\note Please see the specification</p>
execute
mixed FutoIn\RI\AsyncSteps::execute()
Start execution of AsyncSteps, (*73)
It can be called only on root instance of AsyncSteps, (*74)
cancel
mixed FutoIn\RI\AsyncSteps::cancel()
Cancel execution of AsyncSteps, (*75)
It can be called only on root instance of AsyncSteps, (*76)
loop
mixed FutoIn\RI\AsyncSteps::loop(callable $func, string $label)
Execute loop until as.break() is called, (*77)
Arguments
- $func callable - <p>loop body callable( as )</p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
loopForEach
mixed FutoIn\RI\AsyncSteps::loopForEach(array $maplist, callable $func, string $label)
For each map or list element call func( as, key, value ), (*78)
Arguments
- $maplist array
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
repeat
mixed FutoIn\RI\AsyncSteps::repeat(integer $count, callable $func, string $label)
Call func(as, i) for count times, (*79)
Arguments
- $count integer - <p>how many times to call the <em>func</em></p>
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
breakLoop
mixed FutoIn\RI\AsyncSteps::breakLoop(string $label)
Break execution of current loop, throws exception, (*80)
Arguments
- $label string - <p>unwind loops, until <em>label</em> named loop is exited</p>
continueLoop
mixed FutoIn\RI\AsyncSteps::continueLoop(string $label)
Ccontinue loop execution from the next iteration, throws exception, (*81)
Arguments
- $label string - <p>break loops, until <em>label</em> named loop is found</p>
__set
mixed FutoIn\RI\AsyncSteps::__set(string $name, mixed $value)
state() access through AsyncSteps interface / set value, (*82)
Arguments
- $name string - <p>state variable name</p>
- $value mixed - <p>state variable value</p>
__get
mixed FutoIn\RI\AsyncSteps::__get(string $name)
state() access through AsyncSteps interface / get value, (*83)
Arguments
- $name string - <p>state variable name</p>
__isset
boolean FutoIn\RI\AsyncSteps::__isset(string $name)
state() access through AsyncSteps interface / check value exists, (*84)
Arguments
- $name string - <p>state variable name</p>
__unset
mixed FutoIn\RI\AsyncSteps::__unset(string $name)
state() access through AsyncSteps interface / delete value, (*85)
Arguments
- $name string - <p>state variable name</p>
, (*86)
Wrapper interface for singleton AsyncTools implementation, (*87)
- Class name: AsyncTool
- Namespace: FutoIn\RI
Methods
init
mixed FutoIn\RI\AsyncTool::init(\FutoIn\RI\Details\AsyncToolImpl $impl)
Install Async Tool implementation, (*88)
- Visibility: public
- This method is static.
Arguments
callLater
mixed FutoIn\RI\AsyncTool::callLater(callable $cb, integer $delay_ms)
Schedule $cb for later execution after $delays_ms milliseconds, (*89)
- Visibility: public
- This method is static.
Arguments
- $cb callable - <p>Callable to execute</p>
- $delay_ms integer - <p>Required delay in milliseconds</p>
cancelCall
boolean FutoIn\RI\AsyncTool::cancelCall(mixed $ref)
Cancel previously scheduled $ref item, (*90)
- Visibility: public
- This method is static.
Arguments
- $ref mixed - <p>Any value returned from callLater()</p>
, (*91)
Async Tool implementation for testing purposes, (*92)
Install like \FutoIn\RI\AsyncToolTest::init()., (*93)
The primary feature is predictive event firing for debugging
and Unit Testing through nextEvent(), (*94)
Methods
nextEvent
mixed FutoIn\RI\AsyncToolTest::nextEvent()
Wait and execute the next item in queue, if any, (*95)
- Visibility: public
- This method is static.
hasEvents
boolean FutoIn\RI\AsyncToolTest::hasEvents()
Check if any item is scheduled (for unit testing), (*96)
- Visibility: public
- This method is static.
resetEvents
mixed FutoIn\RI\AsyncToolTest::resetEvents()
Reset event queue (for unit testing), (*97)
- Visibility: public
- This method is static.
getEvents
array FutoIn\RI\AsyncToolTest::getEvents()
Get internal item queue (for unit testing), (*98)
- Visibility: public
- This method is static.
run
mixed FutoIn\RI\AsyncToolTest::run()
Run event loop until last event pending, (*99)
- Visibility: public
- This method is static.
__construct
mixed FutoIn\RI\Details\AsyncToolImpl::__construct()
Any derived class should call this c-tor for future-compatibility, (*100)
init
mixed FutoIn\RI\Details\AsyncToolImpl::init()
Install Async Tool implementation, call using derived class, (*101)
callLater
mixed FutoIn\RI\Details\AsyncToolImpl::callLater(callable $cb, integer $delay_ms)
Schedule $cb for later execution after $delays_ms milliseconds, (*102)
Arguments
- $cb callable - <p>Callable to execute</p>
- $delay_ms integer - <p>Required delay in milliseconds</p>
cancelCall
mixed FutoIn\RI\Details\AsyncToolImpl::cancelCall(mixed $ref)
Cancel previously scheduled $ref item, (*103)
Arguments
- $ref mixed - <p>Any value returned from callLater()</p>
, (*104)
FutoIn\RI\Details\AsyncStepsProtection
Internal class to organize AsyncSteps levels during execution, (*105)
- Class name: AsyncStepsProtection
- Namespace: FutoIn\RI\Details
- This class implements: FutoIn\AsyncSteps
Properties
$root
private mixed $root
$adapter_stack
private mixed $adapter_stack
$_onerror
public mixed $_onerror
$_oncancel
public mixed $_oncancel
$_queue
public mixed $_queue = null
$_limit_event
public mixed $_limit_event = null
Methods
__construct
mixed FutoIn\RI\Details\AsyncStepsProtection::__construct($root, $adapter_stack)
Arguments
- $root mixed
- $adapter_stack mixed
_cleanup
mixed FutoIn\RI\Details\AsyncStepsProtection::_cleanup()
_sanityCheck
mixed FutoIn\RI\Details\AsyncStepsProtection::_sanityCheck()
add
mixed FutoIn\RI\Details\AsyncStepsProtection::add(callable $func, callable $onerror)
Arguments
- $func callable
- $onerror callable
parallel
mixed FutoIn\RI\Details\AsyncStepsProtection::parallel(callable $onerror)
Arguments
state
mixed FutoIn\RI\Details\AsyncStepsProtection::state()
success
mixed FutoIn\RI\Details\AsyncStepsProtection::success()
successStep
mixed FutoIn\RI\Details\AsyncStepsProtection::successStep()
error
mixed FutoIn\RI\Details\AsyncStepsProtection::error($name, $error_info)
Arguments
- $name mixed
- $error_info mixed
setTimeout
mixed FutoIn\RI\Details\AsyncStepsProtection::setTimeout($timeout_ms)
Arguments
__invoke
mixed FutoIn\RI\Details\AsyncStepsProtection::__invoke()
setCancel
mixed FutoIn\RI\Details\AsyncStepsProtection::setCancel(callable $oncancel)
Arguments
execute
mixed FutoIn\RI\Details\AsyncStepsProtection::execute()
cancel
mixed FutoIn\RI\Details\AsyncStepsProtection::cancel()
copyFrom
mixed FutoIn\RI\Details\AsyncStepsProtection::copyFrom(\FutoIn\AsyncSteps $other)
Arguments
__clone
mixed FutoIn\RI\Details\AsyncStepsProtection::__clone()
loop
mixed FutoIn\RI\Details\AsyncStepsProtection::loop(callable $func, string $label)
Execute loop until as.break() is called, (*106)
Arguments
- $func callable - <p>loop body callable( as )</p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
loopForEach
mixed FutoIn\RI\Details\AsyncStepsProtection::loopForEach(array $maplist, callable $func, string $label)
For each map or list element call func( as, key, value ), (*107)
Arguments
- $maplist array
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
repeat
mixed FutoIn\RI\Details\AsyncStepsProtection::repeat(integer $count, callable $func, string $label)
Call func(as, i) for count times, (*108)
Arguments
- $count integer - <p>how many times to call the <em>func</em></p>
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
breakLoop
mixed FutoIn\RI\Details\AsyncStepsProtection::breakLoop(string $label)
Break execution of current loop, throws exception, (*109)
Arguments
- $label string - <p>unwind loops, until <em>label</em> named loop is exited</p>
continueLoop
mixed FutoIn\RI\Details\AsyncStepsProtection::continueLoop(string $label)
Ccontinue loop execution from the next iteration, throws exception, (*110)
Arguments
- $label string - <p>break loops, until <em>label</em> named loop is found</p>
__set
mixed FutoIn\RI\Details\AsyncStepsProtection::__set(string $name, mixed $value)
state() access through AsyncSteps interface / set value, (*111)
Arguments
- $name string - <p>state variable name</p>
- $value mixed - <p>state variable value</p>
__get
mixed FutoIn\RI\Details\AsyncStepsProtection::__get(string $name)
state() access through AsyncSteps interface / get value, (*112)
Arguments
- $name string - <p>state variable name</p>
__isset
boolean FutoIn\RI\Details\AsyncStepsProtection::__isset(string $name)
state() access through AsyncSteps interface / check value exists, (*113)
Arguments
- $name string - <p>state variable name</p>
__unset
mixed FutoIn\RI\Details\AsyncStepsProtection::__unset(string $name)
state() access through AsyncSteps interface / delete value, (*114)
Arguments
- $name string - <p>state variable name</p>
, (*115)
Abstract base for AsyncTool implementation, (*116)
- Class name: AsyncToolImpl
- Namespace: FutoIn\RI\Details
- This is an abstract class
Methods
__construct
mixed FutoIn\RI\Details\AsyncToolImpl::__construct()
Any derived class should call this c-tor for future-compatibility, (*117)
init
mixed FutoIn\RI\Details\AsyncToolImpl::init()
Install Async Tool implementation, call using derived class, (*118)
- Visibility: public
- This method is static.
callLater
mixed FutoIn\RI\Details\AsyncToolImpl::callLater(callable $cb, integer $delay_ms)
Schedule $cb for later execution after $delays_ms milliseconds, (*119)
- Visibility: public
- This method is abstract.
Arguments
- $cb callable - <p>Callable to execute</p>
- $delay_ms integer - <p>Required delay in milliseconds</p>
cancelCall
mixed FutoIn\RI\Details\AsyncToolImpl::cancelCall(mixed $ref)
Cancel previously scheduled $ref item, (*120)
- Visibility: public
- This method is abstract.
Arguments
- $ref mixed - <p>Any value returned from callLater()</p>
, (*121)
FutoIn\RI\Details\ParallelStep
Internal class to organize Parallel step execution, (*122)
- Class name: ParallelStep
- Namespace: FutoIn\RI\Details
- This class implements: FutoIn\AsyncSteps
Properties
$root
private mixed $root
$as
private mixed $as
$parallel_steps
private mixed $parallel_steps
$complete_count
private mixed $complete_count
Methods
__construct
mixed FutoIn\RI\Details\ParallelStep::__construct($root, $async_iface)
Arguments
- $root mixed
- $async_iface mixed
_cleanup
mixed FutoIn\RI\Details\ParallelStep::_cleanup()
add
mixed FutoIn\RI\Details\ParallelStep::add(callable $func, callable $onerror)
Arguments
- $func callable
- $onerror callable
parallel
mixed FutoIn\RI\Details\ParallelStep::parallel(callable $onerror)
Arguments
state
mixed FutoIn\RI\Details\ParallelStep::state()
success
mixed FutoIn\RI\Details\ParallelStep::success()
successStep
mixed FutoIn\RI\Details\ParallelStep::successStep()
error
mixed FutoIn\RI\Details\ParallelStep::error($name, $errorinfo)
Arguments
- $name mixed
- $errorinfo mixed
__invoke
mixed FutoIn\RI\Details\ParallelStep::__invoke()
setTimeout
mixed FutoIn\RI\Details\ParallelStep::setTimeout($timeout_ms)
Arguments
setCancel
mixed FutoIn\RI\Details\ParallelStep::setCancel(callable $oncancel)
Arguments
execute
mixed FutoIn\RI\Details\ParallelStep::execute()
copyFrom
mixed FutoIn\RI\Details\ParallelStep::copyFrom(\FutoIn\AsyncSteps $other)
Arguments
__clone
mixed FutoIn\RI\Details\ParallelStep::__clone()
loop
mixed FutoIn\RI\Details\ParallelStep::loop(callable $func, string $label)
Execute loop until as.break() is called, (*123)
Arguments
- $func callable - <p>loop body callable( as )</p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
loopForEach
mixed FutoIn\RI\Details\ParallelStep::loopForEach(array $maplist, callable $func, string $label)
For each map or list element call func( as, key, value ), (*124)
Arguments
- $maplist array
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
repeat
mixed FutoIn\RI\Details\ParallelStep::repeat(integer $count, callable $func, string $label)
Call func(as, i) for count times, (*125)
Arguments
- $count integer - <p>how many times to call the <em>func</em></p>
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
breakLoop
mixed FutoIn\RI\Details\ParallelStep::breakLoop(string $label)
Break execution of current loop, throws exception, (*126)
Arguments
- $label string - <p>unwind loops, until <em>label</em> named loop is exited</p>
continueLoop
mixed FutoIn\RI\Details\ParallelStep::continueLoop(string $label)
Ccontinue loop execution from the next iteration, throws exception, (*127)
Arguments
- $label string - <p>break loops, until <em>label</em> named loop is found</p>
__set
mixed FutoIn\RI\Details\ParallelStep::__set(string $name, mixed $value)
state() access through AsyncSteps interface / set value, (*128)
Arguments
- $name string - <p>state variable name</p>
- $value mixed - <p>state variable value</p>
__get
mixed FutoIn\RI\Details\ParallelStep::__get(string $name)
state() access through AsyncSteps interface / get value, (*129)
Arguments
- $name string - <p>state variable name</p>
__isset
boolean FutoIn\RI\Details\ParallelStep::__isset(string $name)
state() access through AsyncSteps interface / check value exists, (*130)
Arguments
- $name string - <p>state variable name</p>
__unset
mixed FutoIn\RI\Details\ParallelStep::__unset(string $name)
state() access through AsyncSteps interface / delete value, (*131)
Arguments
- $name string - <p>state variable name</p>
, (*132)
FutoIn\RI\Details\StateObject
Internal class to organize state object., (*133)
- Class name: StateObject
- Namespace: FutoIn\RI\Details
Properties
$error_info
public mixed $error_info = null
$last_exception
public mixed $last_exception = null
$_loop_term_label
public mixed $_loop_term_label = null
, (*134)
FutoIn\RI\ScopedSteps
Not standard API. Use for synchronous invocation of AsyncSteps, IF there is no true event loop., (*135)
Example:
$s = new ScopedSteps;
$s->add( ... )->add( ... );
$s->run();, (*136)
\note AsyncTool must be initialized with AsyncToolTest or not initalized at all, (*137)
Methods
__construct
mixed FutoIn\RI\AsyncSteps::__construct(object $state)
Init, (*138)
Arguments
- $state object - <p>for INTERNAL use only</p>
run
mixed FutoIn\RI\ScopedSteps::run()
Execute all steps until nothing is left, (*139)
add
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::add(callable $func, callable $onerror)
Add \$func step executor to end of current AsyncSteps level queue, (*140)
Arguments
- $func callable - <p>void execute_callback( AsyncSteps as[, previous_success_args] )</p>
- $onerror callable - <p>OPTIONAL: void error_callback( AsyncSteps as, error )</p>
copyFrom
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::copyFrom(\FutoIn\RI\AsyncSteps $other)
Copy steps from other AsyncSteps, useful for sub-step cloning, (*141)
Arguments
parallel
\FutoIn\AsyncSteps FutoIn\RI\AsyncSteps::parallel(callable $onerror)
Create special object to queue steps for execution in parallel, (*142)
Arguments
- $onerror callable - <p>OPTIONAL: void error_callback( AsyncSteps as, error )</p>
state
\StdClass FutoIn\RI\AsyncSteps::state()
Access AsyncSteps state object, (*143)
success
mixed FutoIn\RI\AsyncSteps::success()
Set "success" state of current step execution, (*144)
successStep
mixed FutoIn\RI\AsyncSteps::successStep()
Call success() or add efficient dummy step equal to as.success() in behavior
(depending on presence of other sub-steps), (*145)
error
mixed FutoIn\RI\AsyncSteps::error(string $name, string $error_info)
Set "error" state of current step execution, (*146)
Arguments
- $name string - <p>Type of error</p>
- $error_info string - <p>Error description to be put into "error_info" state field</p>
setTimeout
mixed FutoIn\RI\AsyncSteps::setTimeout(integer $timeout_ms)
Delay further execution until as.success() or as.error() is called, (*147)
Arguments
- $timeout_ms integer - <p>Timeout in milliseconds</p>
__invoke
mixed FutoIn\RI\AsyncSteps::__invoke()
PHP-specific alias for success(), (*148)
setCancel
mixed FutoIn\RI\AsyncSteps::setCancel($oncancel)
Set cancellation callback, (*149)
Arguments
- $oncancel mixed - <p>void cancel_callback( AsyncSteps as )
\note Please see the specification</p>
execute
mixed FutoIn\RI\AsyncSteps::execute()
Start execution of AsyncSteps, (*150)
It can be called only on root instance of AsyncSteps, (*151)
cancel
mixed FutoIn\RI\AsyncSteps::cancel()
Cancel execution of AsyncSteps, (*152)
It can be called only on root instance of AsyncSteps, (*153)
loop
mixed FutoIn\RI\AsyncSteps::loop(callable $func, string $label)
Execute loop until as.break() is called, (*154)
Arguments
- $func callable - <p>loop body callable( as )</p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
loopForEach
mixed FutoIn\RI\AsyncSteps::loopForEach(array $maplist, callable $func, string $label)
For each map or list element call func( as, key, value ), (*155)
Arguments
- $maplist array
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
repeat
mixed FutoIn\RI\AsyncSteps::repeat(integer $count, callable $func, string $label)
Call func(as, i) for count times, (*156)
Arguments
- $count integer - <p>how many times to call the <em>func</em></p>
- $func callable - <p>loop body <em>func( as, key, value )</em></p>
- $label string - <p>optional label to use for <em>as.break()</em> and <em>as.continue()</em> in inner loops</p>
breakLoop
mixed FutoIn\RI\AsyncSteps::breakLoop(string $label)
Break execution of current loop, throws exception, (*157)
Arguments
- $label string - <p>unwind loops, until <em>label</em> named loop is exited</p>
continueLoop
mixed FutoIn\RI\AsyncSteps::continueLoop(string $label)
Ccontinue loop execution from the next iteration, throws exception, (*158)
Arguments
- $label string - <p>break loops, until <em>label</em> named loop is found</p>
__set
mixed FutoIn\RI\AsyncSteps::__set(string $name, mixed $value)
state() access through AsyncSteps interface / set value, (*159)
Arguments
- $name string - <p>state variable name</p>
- $value mixed - <p>state variable value</p>
__get
mixed FutoIn\RI\AsyncSteps::__get(string $name)
state() access through AsyncSteps interface / get value, (*160)
Arguments
- $name string - <p>state variable name</p>
__isset
boolean FutoIn\RI\AsyncSteps::__isset(string $name)
state() access through AsyncSteps interface / check value exists, (*161)
Arguments
- $name string - <p>state variable name</p>
__unset
mixed FutoIn\RI\AsyncSteps::__unset(string $name)
state() access through AsyncSteps interface / delete value, (*162)
Arguments
- $name string - <p>state variable name</p>