moebius / loop
一个用于编写可以在Amp或React、Slim、CodeIgniter、Laravel和Symfony等框架之间工作的应用程序的稳固的事件循环API。
Requires
- charm/fallback-logger: ^1.0
- charm/util-closuretool: ^1.0
- composer/semver: ^3.3
- moebius/common: ^1.0
- moebius/promise: >=1.0.100
Requires (Dev)
- charm/testing: >=1 <2
Suggests
- ext-ev: For handling more IO events concurrently
- ext-pcntl: For handling process signals
README
一个事件循环API,用于在传统的PHP代码中编写异步代码,同时也能与React或Amp等常见的异步框架无缝协作。
Moebius Loop提供了一个优雅且一致的API,用于处理非阻塞I/O,在传统的同步PHP代码和React或Amp等完全异步框架中都能良好工作。
Laravel示例
Moebius\Loop可以与Laravel或Symfony等大多数框架一起使用。使用这些框架中的异步代码的挑战在于,框架不会等待你的承诺完成。
解决方案是使用Moebius\Loop::await($promise)
函数。只要所有异步代码都是为直接使用Moebius\Loop
或为React或Amp编写的,就可以使用Moebius\Loop
来解析承诺。
<?php namespace App\Http\Controllers; use Moebius\Loop; use App\Http\Controllers\Controller; use App\Models\User; use App\Models\Profile; class UserController extends Controller { public function show($id) { /** * Go get a performance boost: * * We MUST use async functions which return a promise, * and internally use the event-loop API to wait for * IO resources to become readable or writable. */ $user = User::asyncFindOrFail($id); $profile = Profile::asyncFindOrFail($id); /** * Also we SHOULD send off as many async calls as possible * before using the `Loop::await()` function to resolve * these promises. * * Both promises we created above will be running while you * await the `$user` promise. The `$profile` promise may * even finish first - but that does not matter below: */ return view('user.profile', [ 'user' => Loop::await($user), 'profile' => Loop::await($profile), ]); } }
正如你所看到的,在任意框架中都可以轻松使用异步代码。
API参考
Moebius\Loop::getTime()
获取当前事件循环时间。这是一个从任意时间点起的时间数(以秒为单位)。
Moebius\Loop::await(object $promise, float $timeout=null): mixed
运行事件循环,直到承诺解决或超时。
Moebius\Loop::run(Closure $shouldResumeFunc=null): void
运行事件循环,直到调用Moebius\Loop::stop()
。如果提供了回调,则循环将一直运行,直到回调返回一个falsey值。
Moebius\Loop::delay(float $time, Closure $callback): Closure
在$time
秒后运行回调。返回的回调将取消定时器。
Moebius\Loop::interval(float $interval, Closure $callback): Closure
在$interval
秒后运行回调,并且每隔$interval
秒重复调用一次回调。返回的回调将取消间隔。
Moebius\Loop::readable(resource $resource, Closure $callback): Closure
在读取流资源不会阻塞的情况下,在每次tick时运行回调。这也可以用于在服务器套接字上接受新的连接。返回的回调将取消监视流。
Moebius\Loop::writable(resource $resource, Closure $callback): Closure
在写入流资源不会阻塞的情况下,在每次tick时运行回调。返回的回调将取消监视流。
Moebius\Loop::read(resource $resource, Closure $callback): Closure
从流资源读取数据块,并用读取的数据调用$callback
。此函数将在EOF被调用时停止,除非先调用返回的回调来取消读取操作。
Moebius\Loop::signal(int $signalNumber, Closure $callback): Closure
在进程收到信号时运行回调。回调将每次收到信号时继续被调用,直到调用返回的回调。
示例
一个原始示例,说明如何使用Moebius\Loop
编写异步代码。
此代码可以在Amp和React上运行,如果你在一个更传统的环境(如Laravel或Symfony)中没有使用它们。
<?php require('vendor/autoload.php'); /** * This function returns a Promise about something that will * be available eventually. It will immediately return a Promise * object which is a *promise about a future value*. */ function async_read_file(string $filename) { return new Moebius\Promise(function($ready, $failure) use ($filename) { // Open the file in read non-blocking mode $fp = fopen($filename, 'rn'); // Wait for the file to become readable Moebius\Loop::readable($fp, function($fp) use ($ready) { // Call the $ready callback with the value $ready(stream_get_contents($fp)); // Close the file fclose($fp); }); }); } /** * Now we will read two files in parallel using our above function. */ $file1 = async_read_file('file-1.txt'); $file2 = async_read_file('file-2.txt'); /** * ALTERNATIVE 1 * * The traditional way of waiting for results from a promise is via * the "then" method. This can lead to the well known "callback hell". */ $file1->then(function($contents) { echo "FILE 1: ".$contents."\n\n"; }, function($error) { echo "Failed to read file 1\n"; }); $file2->then(function($contents) { echo "FILE 2: ".$contents."\n\n"; }, function($error) { echo "Failed to read file 1\n"; }); /** * ALTERNATIVE 2 * * A much easier approach to waiting for promises is to use the * `Moebius\Loop::await()` function. It will block your application, * while allowing promises to run - until the promise is fulfilled * or rejected. */ echo "FILE 1: ".Moebius\Loop::await($file1); echo "FILE 2: ".Moebius\Loop::await($file2);
整个API
Moebius\Loop::defer(callable $callback)
将安排回调在下一次执行。
Moebius\Loop::queueMicrotask(callable $callback)
将尽快安排回调执行 - 在任何延迟回调之前。
Moebius\Loop::delay(float $time, callable $callback): EventHandle
将安排回调稍后执行。
Moebius\Loop::readable(resource $fd, callable $callback): EventHandle
将安排回调在 $fd
变为可读时执行。
Moebius\Loop::writable(resource $fd, callable $callback): EventHandle
将安排回调在 $fd
变为可写时执行。
Moebius\Loop::signal(int $signum, callable $callback): EventHandle
将安排回调在应用程序接收到信号时执行。
Moebius\Loop::run(callable $keepRunningFunc=null): void
将运行事件循环,直到 $keepRunningFunc
返回 false 或事件循环为空。
EventHandle 类
当订阅事件(IO、定时器、间隔或信号)时,您将接收到一个 EventHandle 类。此句柄可用于挂起或取消事件监听器。
示例
$readable = Moebius\Loop::readable($fp, function($fp) { // read stream }); // Disable listening for events $readable->suspend(); // Enable listening for events $readable->resume(); // Cancel listening for events (can't be resumed) $readable->cancel();