choval / async
一组帮助在 async 中编码的函数
Requires
- php: ^7.2|^8.0|^8.1
- choval/zombie-reaper: ^1.0
- react/child-process: ^0.6
- react/event-loop: ^1.2|^1.3
- react/promise: ^2.8|^2.9
- react/promise-stream: ^1.3
Requires (Dev)
- phpunit/phpunit: ^9
Suggests
- ext-pcntl: Required to create childs to async blocking code
- ext-posix: Required to kill hanging processes
- ext-sockets: Required to create a channel for communication with child processes
- dev-master
- v0.7.1
- v0.7.0
- v0.6.3
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.32
- v0.5.31
- v0.5.30
- v0.5.29
- v0.5.28
- v0.5.27
- v0.5.26
- v0.5.25
- v0.5.24
- v0.5.23
- v0.5.22
- v0.5.21
- v0.5.20
- v0.5.19
- v0.5.18
- v0.5.17
- v0.5.16
- v0.5.15
- v0.5.14
- v0.5.13
- v0.5.12
- v0.5.11
- v0.5.10
- v0.5.9
- v0.5.8
- v0.5.7
- v0.5.6
- v0.5.5
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.36
- v0.4.35
- v0.4.34
- v0.4.33
- v0.4.32
- v0.4.31
- v0.4.30
- v0.4.29
- v0.4.28
- v0.4.27
- v0.4.26
- v0.3.17
This package is auto-updated.
Last update: 2023-12-17 02:14:22 UTC
README
Choval\Async
一个库,用于简化在 ReactPHP 中处理承诺
安装
composer require choval/async
要求
- PHP 8.0+
- 对于
async
&execute
- ext-pcntl
- ext-posix
- ext-sockets
用法
以下是一个函数的示例
function future($i=0) { return new React\Promise\FulfilledPromise($i+1); }
丑陋的方式
future() ->then(function ($i) { return future($i); }) ->then(function ($i) { return future($i); }) ->then(function ($i) { return future($i); }) ->then(function ($i) { return future($i); }) ->then(function ($i) { echo $i; }); // Prints 5, but that chain nightmare...
使用 yield
,记住 future()
是返回一个 Promise
。
并且在循环中不会阻塞其他事件 ;-)
Async\resolve(function () { $i = yield future(); $i = yield future($i); $i = yield future($i); $i = yield future($i); $i = yield future($i); echo $i; }); // Prints 5 as well ;-)
或者在while循环中
Async\resolve(function () { $i = 0; while($i<5) { $i = yield future($i); } echo $i; });
函数
is_done
检查一个 Promise
是否已被 解决 或 拒绝。这返回一个布尔值,而不是一个 Promise
。
$defer = new React\Promise\Deferred(); $loop->addTimer(1, function () use ($defer) { $defer->resolve(true); }); $promise = $defer->promise(); $i = 0; function future($i=0) { return new React\Promise\FulfilledPromise($i+1); } while(!Async\is_done($promise)) { $i++; } echo "Promise finished with $i loops\n";
resolve
这就是让您 yield
承诺的方式,它类似于 Node.js await
。
$promise = Async\resolve(function () { yield 1; yield 2; return 'Wazza'; }); // $promise resolves with Wazza
例如以下异步事件。
$defer1 = new React\Promise\Deferred(); $loop->addTimer(1, function () use ($defer1) { $defer1->resolve('hello'); }); $defer2 = new React\Promise\Deferred(); $loop->addTimer(0.5, function () use ($defer2) { $defer2->resolve('world'); }); $promise = Async\resolve(function () use ($defer1, $defer2) { $out = []; $out[] = yield $defer1->promise(); $out[] = yield $defer2->promise(); return implode(' ', $out); });
$promise
在1秒后解决为 hello world
,尽管第二个承诺首先解决。
如果您需要同时运行多个异步操作怎么办?
$promise = Async\resolve(function () { $fetch = [ 'bing' => Async\execute('curl https://bing.com/'), 'duckduckgo' => Async\execute('curl https://duckduckgo.com/'), 'google' => Async\execute('curl https://google.com/'), ]; $sources = yield React\Promise\all($fetch); return $sources; });
内存使用
silent
类似于 resolve
,但会捕获任何 Exception
并将其保存为第二个参数。
如果失败,承诺将以 null 解决。
$fn = function () { throw new \Exception('hey!'); }; $promise = Async\silent($fn, $e); // Promise resolves with null // $e will hold an the hey! exception
execute
异步执行命令。
返回一个包含命令输出的 Promise
。
Async\execute('echo "Wazza"') ->then(function ($output) { // $output contains Wazza\n }) ->otherwise(function ($e) { // Throws an Exception if the execution fails // ie: 127 if the command does not exist $exitCode = $e->getCode(); });
可以传递一个以秒为单位的 timeout
参数。
sleep
一个异步的 sleep
函数。这不会阻塞其他事件。
$promise = Async\resolve(function () { $start = time(); yield Async\sleep(2); $end = time(); return $end-$start; }); // $promise resolves in ~2 seconds
记住这是一个非阻塞的 sleep
,如果你不在 Async\resolve 内等待它或 yield,则 Promise
将在后台解决。
$start = time(); Async\sleep(2); $end = time(); // $start and $end will be the same
wait
也称为 sync
,使异步代码阻塞。在需要使用异步库的同步/阻塞场景中使用此功能。
此函数接收以下之一:Generator
、Closure
或 PromiseInterface
。
$start = time(); Async\wait(Async\sleep(2)); $end = time(); // $end == $start+2;
第二个浮点参数是秒数超时,默认无超时。
第三个浮点参数是检查的间隔,默认为 0.01 秒。间隔低将消耗更多 CPU。
async
需要在一个异步环境中运行一段阻塞代码?使用这个,但请注意它使用 pcntl_fork
。
第一个参数是可调用的,第二个参数是可调用参数的数组。
$blocking_code = function ($secs) { sleep($secs); return time(); } $secs = 1; $promises = []; $promises[] = Async\async($blocking_code, [$secs]); $promises[] = Async\async($blocking_code, [$secs]); $promises[] = Async\async($blocking_code, [$secs]); $promises[] = Async\async($blocking_code, [$secs]); $base = time()+$secs; $times = Async\wait(React\Promise\all($promises)); foreach ($times as $time) { // $time === $base }
同时运行的异步子进程数量有限制为 50。可以通过调用 Async\set_forks_limit
来更改此限制。
此限制也适用于 Async\execute
。
Async\set_forks_limit(100); echo Async\get_forks_limit(); // 100
当达到限制时,代码将等待任何之前的子进程完成后再继续,保持异步子进程数不超过设置的子进程限制。
retry
运行一个 函数(Closure/Generator)直到 retries
次返回“良好”。否则,返回最后一个异常。
此函数还可以忽略一组异常类或消息。
$times = 5; $func = function () use (&$times) { if(--$times) { throw new \Exception('bad error'); } return 'ok'; }; $retries = 6; Async\retry($func, $retries, 0.1, 'bad error') ->then(function ($res) { // $res is 'ok' });
/** * @param callable $func * @param int $retries=10 (optional) * @param float $frequency=0.001 (optional) * @param string $ignoress (optional) The Throwable class to catch or string to match against Exception->getMessage() * * @return Promise */
timeout
类似于 React\Promise\Timer\timeout()
,但允许 Generator
或 Closure
。
$func = function () { yield Async\sleep(2); return true; }; Async\wait(Async\timeout($func, 1.5)); // Throws an Exception due to the timeout 1.5 < 2
timer
保存已过微秒数(浮点数)。
Async\wait(function () { Async\timer(function () { Async\sleep(0.1); }, $msecs); print_r($msecs); // ~100ms });
wait_memory
等待可用的一定数量的内存字节数。
这用于循环内部,以避免由于创建和留下多个 Promise 而导致的内存耗尽。
Async\wait(function () { $loop = 20000; $mem = 1024*1024*16; // 16MB while($loop--) { yield Async\waitMemory($mem); Async\sleep(1); } });
可以传递第二个参数作为运行检查的频率。
返回剩余的字节数(memory_limit
- memory_get_usage()
)。
rglob
具有忽略参数的递归 glob
。
考虑以下文件
/files/
/files/a.txt
/files/b.txt
/files/a.php
/files/b.php
/files/c.php
/files/1/a.txt
/files/1/a.php
/files/1/b.php
/files/1/c.php
/files/2/a.php
/files/2/b.php
/files/2/c.php
$files = Async\wait(Async\rglob('/files/*.php', 'a')); /* $files has: /files/b.php /files/c.php /files/1/b.php /files/1/c.php /files/2/b.php /files/2/c.php */
PHP 文件函数
以下函数与它们的 PHP 版本具有相同的参数,但使用 Async\async
运行,并可选择将 LoopInterface
作为其第一个参数。
这些尚未经过生产测试/优化。请谨慎使用。
file_get_contents
file_put_contents
file_exists
is_file
is_dir
is_link
sha1_file
md5_file
mime_content_type
realpath
fileatime
filectime
filemtime
file
filesize
copy
rename
unlink
touch
mkdir
rmdir
scandir
glob
示例
$lines = Async\wait(Async\file('/etc/hosts')); var_dump($lines);
许可
MIT,请参阅 LICENSE。