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.pharfile 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$envreturns a Promise resolved, return its result. - Throws:
{Throwable}if$envthrows 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 ofawaitenvironment until it returns a truthfully value, sent in$thenas second parameter; if function throws an error, send it to$thenas first parameter.$then {Closure.<$error {Throwable|null}, $result {*}>}is the function executed right after an event happened in$fn; when$fnresolves, the result is sent as second parameter$resultor when$fnthrows error, the error is sent as first parameter$error(else$errorisnull).
- Returns:
null. - Throws:
{Async\AsyncError}if this instance is not created in anawaitcontext.
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$envthrows.
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$envreturns a Promise resolved, return its result. - Throws:
{Throwable}if$envthrows 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}trueif 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 ofawaitenvironment until it returns a truthfully value, sent in$thenas second parameter; if function throws an error, send it to$thenas first parameter.$then {Closure.<$error {Throwable|null}, $result {*}>}is the function executed right after an event happened in$test; when$testresolves, the result is sent as second parameter$resultor when$testthrows error, the error is sent as first parameter$error(else$errorisnull).
- Returns:
{Async\Async}new instance. - Throws:
{Async\AsyncError}if this instance is not created in anawaitcontext.
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$resolveexecution or$rejectexecution 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$resultvalue 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$errorvalue 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$resultvalue is sent in$resolveor 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.