It's now available! Just start your asynchronous environment with await
function, then do whatever you want with functions and classes below!
You can :
- run
composer require amonite/async
, or - copy
async.phar
file in your project directory and load it with arequire
.
As a JavaScript developer, I like the asynchronous paradigm. I decided to export this paradigm to allow PHP to take advantage of it: now, produce libraries that can process information while waiting for them to load data.
In PHP, by default many functions stop script until the end. That's helpful with common arithmetic functions, but you wan also observe it when you request a data from your database (no matter your software), when you load a file from your computer or internet, when you use some socket, or run a command-line.
When your script wait for an other software, then your process is down. When a client start a request to the server, he waits for the previous client end of script. When thousands clients wait for a single client who waits for an only one heavy SQL request with 30+ seconds duration, then the server could crash as if it was a DDOS attack.
By using this library, I will develop a non-blocking HTTP server, then HTTPS, HTTP/2, SQL and filesystem reader/writer. Hope it will help!
Function await
create an environment that allows to register an async
event. An asynchronous event is an non-blocking function that continue script until end of a writing file function, a reading file function, connection start function and so on.
The function $fn_env
in await($fn_env) : mixed
create an asynchronous environment, and wait (many ticks) for all asynchronous events ends. The script is blocked in this function until the end of its async functions. If $fn_env
returns a Promise
, the await
function returns its anwser resolved or throw its error rejected.
File wrapper test/wrap.php
:
<?php
require_once "async.phar";
//require_once "index.php";
await(function () {
require "await.php";
require "async.php";
});
The function $test
in async($test, $then) : null
is executed each tick of await
function until it returns a truthfully value (or throws an error).
Here the file wrap
let all required files to use async functions.
In the next file, I combined both async and await. First async is registered, then we wait for await
end of function and async events, and then register the last async function.
File test/await.php
:
<?php
$date = microtime(true);
async(function () use ($date) { if($date + 4 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "first resolved\n"; });
echo "first entered\n";
await(function () use ($date) {
async(function () use ($date) { if($date + 3 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "second resolved\n"; });
echo "second entered\n";
async(function () use ($date) { if($date + 2 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "third resolved\n"; });
echo "third entered\n";
});
async(function () use ($date) { if($date + 1 < microtime(true)) return true; }, function ($err) { if ($err) echo "err:$err\n"; echo "fourth resolved\n"; });
echo "fourth entered\n";
The console will print:
(t+0s) > first entered
(t+0s) > second entered
(t+0s) > third entered
(t+2s) > third resolved
(t+3s) > second resolved
(t+3s) > fourth entered
(t+3s) > fourth resolved
(t+4s) > first resolved
A Promise is commonly used to flat asynchronous and verbose functions. It follows JavaScript standard.
File test/promise.php
:
<?php
require_once __DIR__ . "/../index.php";
$date = microtime(true);
$p = new Async\Promise(function ($resolve, $reject) use ($date) {
while (microtime(true) < $date + 2) time_nanosleep(0,1);
$resolve();
});
$p->then(function () { echo "I waited for 2 seconds.\n"; throw new Error("test error"); });
$p->catch(function () { echo "An error occurred in the promise or in then() function.\n"; });
var_dump(await(function () use ($date) {
return Async\Promise::all(array(
Async\Promise::resolve(3.14),
Async\Promise::async(function () { return "Fibonacci"; }),
Async\Promise::async(function () use ($date) {
if (microtime(true) > $date + 3) return "Time elapsed!";
})
));
}));
(!) In this example, the code is blocked until the 2 seconds passed, then wait for every Promise
to be resolved.
If you want to full manage your async events, you may prefer use the class Async.
File test/async.php
:
<?php
$delay = microtime(true) + 3;
$test = function () use ($delay) {
if ($delay < microtime(true)) return "delay";
};
$then = function ($err, $m) {
if ($err) {
echo "$err err";
} else {
echo "$m then\n";
}
};
new Async\Async($test, $then);
Now you've been played with asynchronous items, you can return in a synchronous script, with its blocking function and its wasting of time. Lucky you! You can get await
value by returning a Promise
in its environment. Function await
is able to get result of a Promise returned. Warning: if your Promise reject, then await
will throw the error!
File test/await_async.php
:
<?php
require_once "index.php";
$res = await(function () {
// do async stuff
// ...
return new Async\Promise(function (\Closure $resolve, \Closure $reject) {
$d = 3;
$t = microtime(true) + $d;
async(function () use ($t){
return microtime(true) > $t;
}, function ($err) use ($resolve, $reject, $d) {
if ($err) $reject($err);
else $resolve("Now $d seconds elapsed. Well done!\n");
});
});
});
echo $res;
Create an async instance and register it in await context.
- Syntax:
await (\Closure $env) : mixed
- Parameter:
$env {Closure.<$self {Closure}>}
is a function executed to register every async instances. - Returns:
{*|null}
if$env
returns a Promise resolved, return its result. - Throws:
{Throwable}
if$env
throws or returns a Promise rejected (return its error).
Register a function that wait to be resolved or rejected each ticks of await
environment or a timeout error (3000ms if not unset).
- Syntax:
async (\Closure $test, \Closure $then = null) : null
- Parameters:
$fn {Closure.<>}
is a first function executed each ofawait
environment until it returns a truthfully value, sent in$then
as second parameter; if function throws an error, send it to$then
as first parameter.$then {Closure.<$error {Throwable|null}, $result {*}>}
is the function executed right after an event happened in$fn
; when$fn
resolves, the result is sent as second parameter$result
or when$fn
throws error, the error is sent as first parameter$error
(else$error
isnull
).
- Returns:
null
. - Throws:
{Async\AsyncError}
if this instance is not created in anawait
context.
Create an async instance and register it in await context.
- Syntax:
$await = new Async\Await (\Closure $env) : {Async\Await}
- Parameter:
$env {Closure.<$self {Closure}>}
is a function executed to register every async instances. - Returns:
{Async\Await}
new instance. - Throws:
{Throwable}
if$env
throws.
Run function in await context, and add async instances found in $env
.
- Syntax:
$await->env (\Closure $env) : mixed
- Parameter:
$env {Closure.<$self {Closure}>}
is a function executed to register every async instances. - Returns:
{*|null}
if$env
returns a Promise resolved, return its result. - Throws:
{Throwable}
if$env
throws or returns a Promise rejected (return its error).
Tell if your script is currently in an await
context.
- Syntax:
Async\Await::isAwaitContext() : boolean
- Returns:
{boolean}
true
if script is in an await context.
Register a function that wait to be resolved or rejected each ticks of await
environment or a timeout error (3000ms if not unset).
- Syntax:
$async = new Async\Async (\Closure $test, \Closure $then = null) : Async\Async
- Parameters:
$test {Closure.<>}
is a first function executed each ofawait
environment until it returns a truthfully value, sent in$then
as second parameter; if function throws an error, send it to$then
as first parameter.$then {Closure.<$error {Throwable|null}, $result {*}>}
is the function executed right after an event happened in$test
; when$test
resolves, the result is sent as second parameter$result
or when$test
throws error, the error is sent as first parameter$error
(else$error
isnull
).
- Returns:
{Async\Async}
new instance. - Throws:
{Async\AsyncError}
if this instance is not created in anawait
context.
Verify that the $test
function returns a truthfully answer: if it is truthfully, execute $then
function; else do nothing; or catch error and send it to $then
function.
- Syntax:
$async->test()
- Returns:
{Async\Async}
this instance.
Create a Promise instance. Promise helps to flat async functions in a single instance.
- Syntax:
$prom = new Async\Promise(\Closure $fn) : Async\Promise
- Parameter:
$fn {Closure.<$resolve {Closure.<$result {*}>}, $reject {Closure.<$error {Throwable}>}>}
is the function executed that wait for$resolve
execution or$reject
execution or an error thrown. - Returns:
{Async\Promise.<$result {*}>.<$error {Throwable}>}
new instance.
Register a function to execute when Promise resolves.
- Syntax:
$prom->then(\Closure $fn)
- Parameter:
$fn {Closure.<$result {*}>}
is the function executed when Promise is resolved. The$result
value is sent in$resolve
. - Returns:
{Async\Promise}
self instance.
Register a function to execute when Promise rejects.
- Syntax:
$prom->then(\Closure $fn)
- Parameter:
$fn {Closure.<$error {Throwable}>}
is the function executed when Promise is rejected. The$error
value is sent in$reject
. - Returns:
{Async\Promise}
self instance.
Register a function to execute after Promise resolves or rejects.
- Syntax:
$prom->then(\Closure $fn)
- Parameter:
$fn {Closure.<$result {*}>}
is the function executed after Promise is resolved or rejected. The$result
value is sent in$resolve
or in$reject
. - Returns:
{Async\Promise}
self instance.
Helpful function for creating an already resolved Promise.
- Syntax:
Async\Promise::resolve($result {*})
- Parameter:
$result {*}
is the value resolved - Returns:
{Async\Promise}
new instance.
Helpful function for creating an already rejected Promise.
- Syntax:
Async\Promise::resolve($error {*})
- Parameter:
$error {Throwable}
is the value rejected - Returns:
{Async\Promise}
new instance.
Helpful function for creating a Promise that wait for list $list
of Promise to be resolved.
- Syntax:
Async\Promise::all($list)
- Parameter:
$list {Array.<{Async\Promise} ...>}
a list of Promises that should be resolved. - Returns:
{Array.<{*} ...>}
the answer of each Promises.
Helpful function for creating a Promise that wait for one Promise of list $list
of Promise to be resolved.
- Syntax:
Async\Promise::any($list)
- Parameter:
$list {Array.<{Async\Promise} ...>}
a list of Promises that could be resolved. - Returns:
{*}
the answer of the first resolved Promise.