hiblaphp/async
Composer 安装命令:
composer require hiblaphp/async
包简介
async/await implementation with structured concurrency.
README 文档
README
Context-independent async/await for PHP without function coloring.
hiblaphp/async brings async and await to PHP as plain functions built on top of PHP 8.1 Fibers and the Hibla event loop. Unlike JavaScript, Python, or C#, await() works in both fiber and non-fiber contexts. You write normal functions and lift them into concurrency at the call site, not inside the function definition.
Contents
Getting started
Core usage
async(): Running Code Concurrentlyawait(): Suspending Until a Promise Settles- No Function Coloring in Practice
Features
asyncFn(): Wrapping a Callablesleep(): Async-Aware PauseinFiber(): Context DetectionAsyncEnvironment: Controlling Runtime Strictness- Cancellation inside
async() - Combining with Promise Combinators
Testing
Reference
Meta
Installation
This package is currently in beta. Before installing, ensure your
composer.jsonallows beta releases:
{
"minimum-stability": "beta",
"prefer-stable": true
}
composer require hiblaphp/async
Requirements:
- PHP 8.4+
Quick Example
use function Hibla\async; use function Hibla\await; use function Hibla\sleep; // Fetch three resources concurrently. Each async() runs in its own Fiber. [$user, $orders, $stats] = await(Promise::all([ async(fn() => fetchUser(1)), async(fn() => fetchOrders(1)), async(fn() => fetchStats(1)), ])); // Write sequential async logic that reads like synchronous code $report = await(async(function () { $user = await(fetchUser(1)); $orders = await(fetchOrders($user->id)); sleep(0.5); // Suspends this Fiber while other work continues in the background. return generateReport($user, $orders); })); // Outside a Fiber, await() holds the script here while the event loop // keeps running underneath. Timers fire, and other in-flight work continues. $user = await(fetchUser(1)); echo $user->name;
The four things to notice:
async()runs a block of code in its own Fiber and returns aPromise.await()suspends the current Fiber until a promise settles. When called at the top level, it holds the script at that line while the event loop keeps running underneath.Promise::all()waits for multiple async tasks concurrently and gives you all results at once.- Functions that use
await()need no special marking. The caller decides whether to give them concurrency by wrapping them inasync().
The rest of this document covers each of these in detail.
Introduction
PHP has always been synchronous. When your code calls an HTTP endpoint, reads a file, or queries a database, it blocks and waits. One operation at a time, in sequence, from top to bottom. For short-lived scripts and simple request handlers this is fine. But the moment you need to fetch multiple things at once, handle WebSocket connections, or run background jobs without spinning up new processes, the model falls apart.
The standard solution in most languages is async/await: a way to mark functions as asynchronous and pause them at I/O boundaries while other work proceeds. But every major language that has implemented this (JavaScript, Python, C#) has introduced what is known as function coloring. async and await are syntax keywords that live inside the function definition. The moment a function uses await, it must be marked async, which changes its return type, which forces every caller to also be async. The color spreads upward through the entire call stack, creating two incompatible worlds (sync code and async code) that cannot be mixed freely.
hiblaphp/async solves this differently. async() and await() are plain PHP functions, not keywords. await() is context-independent: it checks whether it is running inside a Fiber at runtime and behaves accordingly. Inside a Fiber it suspends cooperatively. Outside a Fiber it holds the script at that line while the event loop keeps running underneath. Timers fire, I/O callbacks run, and other in-flight work continues normally while it waits. A function that calls await() has no special marking, no changed return type, and no impact on its callers. The caller decides whether to give it concurrency by wrapping it in async() at the call site. The color lives at the call site, not inside the function.
This library is the top of the Hibla async stack. It sits on hiblaphp/event-loop for fiber scheduling, hiblaphp/promise for the promise model, and hiblaphp/cancellation for external cancellation coordination. Together these four libraries give you a complete async programming model for PHP that reads like synchronous code but runs cooperatively under the hood.
The Function Coloring Problem
In JavaScript, Python, and C#, async and await are keywords that live inside the function definition. The moment a function uses await, it must be marked async, which changes its return type, which forces every caller to also be async. The color spreads upward through the entire call stack:
// JavaScript: color spreads upward through every layer async function getUser(id) { // must be async return await fetchUser(id); // uses await } async function buildPage(userId) { // must be async because getUser is async const user = await getUser(userId); return user; } async function handleRequest(req) { // must be async because buildPage is async const page = await buildPage(req.userId); return page; }
Hibla solves this entirely. await() is just a regular PHP function that checks its execution context at runtime. A function that uses await() has no special marking, no changed return type, and no impact on its callers. The caller decides whether to give it concurrency by wrapping it in async() at the call site:
use function Hibla\async; use function Hibla\await; // A plain function with no special marking and no color function getUser(int $id): User { return await(fetchUser($id)); } // Works synchronously at the top level without needing async() $user = getUser(1); // Works concurrently when wrapped in async() without any changes to getUser() $promise = async(fn() => getUser(1));
The color lives at the call site, not inside the function. This means you can write your entire application using normal functions with await() and introduce concurrency selectively where you need it.
Fibers and Coroutines
PHP Fibers were introduced in PHP 8.1 as a first-class stackful coroutine primitive. A stackful coroutine is a unit of execution that can be suspended and resumed at any point in its call stack, including inside deeply nested function calls. This is what separates Fibers from generators.
A generator can only suspend at the top-level yield inside the generator function itself. A Fiber can suspend from anywhere in its call stack. When a Fiber suspends, the entire call stack at that point (every function frame, every local variable, every instruction pointer) is frozen and preserved. When the Fiber is resumed, execution continues from exactly where it left off as if nothing happened.
A Fiber also has its own separate C-level stack, independent from the main thread stack, which is what makes suspension at any depth possible.
async() creates a Fiber and schedules it on the event loop. When the Fiber calls await() on a pending promise, it calls Fiber::suspend() internally, freezing the entire call stack and returning control to the event loop. When the promise resolves, Loop::scheduleFiber() queues the Fiber to be resumed, and the event loop restores the full call stack and continues execution from the suspension point:
function fetchUserProfile(int $id): PromiseInterface { return async(function () use ($id) { $user = await(Http::get("/users/$id")); $avatar = await(Http::get("/avatars/$id")); return ['user' => $user, 'avatar' => $avatar]; }); } async(function () { // Multiple profiles load concurrently because each async() call // runs in its own Fiber and suspends independently at each await() [$page1, $page2, $page3] = await(Promise::all([ fetchUserProfile(1), fetchUserProfile(2), fetchUserProfile(3), ])); });
async(): Running Code Concurrently
async() wraps a callable in a PHP Fiber, schedules it on the event loop, and returns a Promise that resolves with the callable's return value. The callable does not run immediately. It is queued in the Fiber phase of the next event loop iteration:
use function Hibla\async; $promise = async(function () { return 'hello from a fiber'; }); $promise->then(fn($value) => print($value)); // hello from a fiber
Multiple async() calls run concurrently. Each one gets its own Fiber and yields to others at every await() point:
$start = microtime(true); async(function () { await(delay(1)); echo "Task 1 done\n"; }); async(function () { await(delay(1)); echo "Task 2 done\n"; }); async(function () { await(delay(1)); echo "Task 3 done\n"; }); // All three run concurrently. The total time is ~1 second, not 3. echo microtime(true) - $start; // ~1.0
One fiber runs at a time: never block inside async()
The event loop runs only one Fiber at a time. Fibers are cooperatively scheduled. A Fiber runs until it explicitly suspends via await() or sleep(), at which point the event loop picks up the next ready Fiber.
A blocking call inside a Fiber (PHP's native sleep(), a synchronous database query, file_get_contents(), or any other call that blocks the OS thread) stalls the entire event loop for its duration. No other Fiber runs, no timers fire, and no I/O is processed until the blocking call returns:
// Wrong: blocks the entire loop for 2 seconds async(function () { \sleep(2); // PHP's native sleep function stalls everything echo "done\n"; }); // Correct: suspends this Fiber cooperatively so the loop stays free async(function () { sleep(2); // Hibla's sleep function. Use function Hibla\sleep echo "done\n"; });
Always use the async-aware equivalents from the Hibla ecosystem: Http::get() instead of file_get_contents(), await(delay($n)) instead of \sleep($n), stream watchers via hiblaphp/stream instead of blocking fread(). If you need to run genuinely blocking work or CPU-bound tasks, offload them to a separate process via hiblaphp/parallel rather than running them inside a Fiber.
Exceptions inside async()
Any exception thrown inside an async() block rejects the returned promise. Always attach a catch() handler or await() the promise inside a try/catch when you care about errors:
$promise = async(function () { throw new \RuntimeException('Something went wrong'); }); $promise->catch(fn($e) => print($e->getMessage())); // Something went wrong
async(function () { try { $result = await(riskyOperation()); return $result; } catch (\Throwable $e) { logError($e); return null; } });
Avoid unnecessary wrapping
Each async() call creates a new PHP Fiber. Fibers are lightweight but not free. Each one allocates a C-level stack and associated runtime state. Creating a Fiber just to immediately await a single promise that already exists adds overhead with no benefit.
If a function already returns a promise, await() it directly:
// Wrong: allocates a full Fiber just to await one existing promise $result = await(async(fn() => await(Http::get('/api/data')))); // Correct: await the promise directly without needing a Fiber $result = await(Http::get('/api/data'));
The same applies to plain functions that use await() internally. They already work in both sync and async contexts without wrapping:
function getUserName(int $id): string { $user = await(fetchUser($id)); return $user->name; } // Wrong: getUserName() already works in both contexts $name = await(async(fn() => getUserName(1))); // Correct: call it directly $name = getUserName(1); // Only wrap in async() when you specifically want concurrent execution $promise = async(fn() => getUserName(1)); // justified due to explicit concurrency
Use async() when you genuinely need a Fiber. This happens when you need to await multiple promises sequentially with logic in between, or when you want a block of code to run concurrently as its own unit of work:
// Good use: multiple awaits with logic between them $promise = async(function () { $user = await(fetchUser(1)); $orders = await(fetchOrders($user->id)); $ratings = await(fetchRatings($user->id)); return processData($user, $orders, $ratings); });
await(): Suspending Until a Promise Settles
await() suspends the current Fiber until the given promise settles, then returns the resolved value or throws the rejection reason:
use function Hibla\await; $user = await(fetchUser(1)); echo $user->name;
Context-independent behavior
await() checks Fiber::getCurrent() at runtime and behaves accordingly:
- Inside a Fiber (
async()block): suspends the Fiber cooperatively. The event loop continues running, so other fibers, timers, and I/O all proceed while this Fiber waits. - Outside a Fiber (top level or sync function): holds the script at that line and drives the event loop until the promise settles. The event loop remains fully alive underneath. Timers fire, I/O callbacks run, and other in-flight work continues normally while it waits.
// Outside a Fiber: holds the script here while the event loop keeps running underneath $user = await(fetchUser(1)); // Inside a Fiber: suspends cooperatively async(function () { $user = await(fetchUser(1)); // other work runs while waiting echo $user->name; });
This context-independence is what eliminates function coloring. A function that calls await() works correctly regardless of where it is called from. It does not need to know or care whether it is inside a Fiber.
Rejection and cancellation
If the awaited promise rejects, await() throws the rejection reason:
async(function () { try { $user = await(fetchUser(999)); // rejects with NotFoundException } catch (\NotFoundException $e) { echo "User not found\n"; } });
If the promise is cancelled before or during the await, await() throws CancelledException:
async(function () use ($token) { try { $user = await(fetchUser(1), $token); } catch (\Hibla\Promise\Exceptions\CancelledException $e) { echo "Fetch was cancelled\n"; } });
With CancellationToken
Pass a CancellationToken as the second argument to automatically track the promise against the token. If the token is cancelled while the Fiber is suspended, the promise is cancelled and CancelledException is thrown at the await() call site, with no manual token->track() needed:
use Hibla\Cancellation\CancellationTokenSource; use function Hibla\async; use function Hibla\await; $cts = new CancellationTokenSource(5.0); // 5 second timeout async(function () use ($cts) { try { $user = await(fetchUser(1), $cts->token); $orders = await(fetchOrders($user->id), $cts->token); return compact('user', 'orders'); } catch (\Hibla\Promise\Exceptions\CancelledException $e) { echo "Operation timed out or was cancelled\n"; } });
No Function Coloring in Practice
The power of the no-coloring design becomes clear when you write library code that uses await() internally. The same code works in every context without any changes:
// Plain functions using await() internally require no special marking function getUser(int $id): User { return await(Http::get("/users/$id")->then( fn($r) => User::fromArray(json_decode($r->getBody(), true)) )); } function getUserWithOrders(int $id): array { $user = getUser($id); $orders = await(fetchOrders($user->id)); return compact('user', 'orders'); }
These are plain functions. Callers can use them in any of these ways without any changes to the functions themselves:
// 1. Synchronous: holds the script at each call while the event loop keeps running underneath $data = getUserWithOrders(1); // 2. Single async task: runs in a Fiber and suspends cooperatively $promise = async(fn() => getUserWithOrders(1)); // 3. Concurrent: multiple users fetched concurrently $promises = array_map( fn($id) => async(fn() => getUserWithOrders($id)), [1, 2, 3, 4, 5] ); await(Promise::all($promises)); // 4. With concurrency limiting await(Promise::concurrent( array_map( fn($id) => fn() => async(fn() => getUserWithOrders($id)), range(1, 100) ), concurrency: 10 ));
The functions never changed. The concurrency strategy is entirely decided by the caller.
asyncFn(): Wrapping a Callable
asyncFn() wraps a callable so that every call to it automatically runs inside async() and returns a Promise. Useful when you want to convert an existing function into a reusable async factory without changing the original function.
The same performance considerations from the avoid unnecessary wrapping section apply. Only use it when the wrapped function genuinely needs its own Fiber context for concurrent execution:
use function Hibla\asyncFn; function processRecord(array $record): array { $enriched = await(enrichRecord($record)); $validated = await(validateRecord($enriched)); return $validated; } // Create an async version without changing processRecord() $asyncProcess = asyncFn('processRecord'); // Primary use case: passing to Promise::map() or Promise::concurrent() await(Promise::map($records, $asyncProcess, concurrency: 10));
sleep(): Async-Aware Pause
The sleep() function from hiblaphp/async is an async-aware replacement for PHP's native sleep(). It accepts fractional seconds: sleep(0.5) for 500ms, sleep(1.5) for 1.5 seconds.
- Inside a Fiber: suspends the current Fiber cooperatively. The event loop continues, so other fibers, timers, and I/O run while this Fiber waits.
- Outside a Fiber: holds the script at that line while the event loop keeps running underneath. Timers fire, I/O callbacks run, and other in-flight work continues normally while it waits.
use function Hibla\sleep; async(function () { echo "Task 1 start\n"; sleep(2); echo "Task 1 done\n"; }); async(function () { echo "Task 2 start\n"; sleep(1); echo "Task 2 done\n"; // runs before Task 1 }); // Output: // Task 1 start // Task 2 start // Task 2 done (~1 second) // Task 1 done (~2 seconds) // Total time: ~2 seconds, not 3
Important: Always import
Hibla\sleepexplicitly. PHP's nativesleep()and Hibla'ssleep()have the same name. If you forget the import you will silently call PHP's native blockingsleep()instead, stalling the entire event loop with no error or warning:use function Hibla\sleep; // required — do not omit async(function () { sleep(1); // Hibla's sleep — correct \sleep(1); // PHP's native sleep — stalls the entire loop });
inFiber(): Context Detection
inFiber() returns true if the current code is executing inside a PHP Fiber. Useful for writing code that needs to behave differently depending on whether it is in an async context:
use function Hibla\inFiber; function getStatus(): string { if (inFiber()) { return await(fetchStatusAsync()); } return fetchStatusSync(); }
In most cases you will not need this. await() already handles both contexts automatically. inFiber() is primarily useful when you want to select between fundamentally different implementations rather than just different blocking behaviors.
AsyncEnvironment: Controlling Runtime Strictness
Managing safety constraints across asynchronous code can occasionally lead to situations known as Async Boundary Context Loss (or the "Fiber Cliff").
When you execute code synchronously within an active request or job context, the active Fiber is preserved across all nested standard functions, classes, and helper callbacks. However, Fibers do not survive asynchronous boundaries.
The moment you register a callback to be executed later by the Event Loop (such as inside a Loop::delay timer, a Loop::nextTick microtask, or inside some collection combinators like Promise::map without asyncFn), that callback is executed on the main event loop thread ({main}) outside of any Fiber context.
If a developer attempts to call await() or sleep() inside an asynchronous callback on {main}, the engine's default context-independent behavior will detect there is no active Fiber and silently fall back to cooperative blocking. Inside a high-concurrency daemon like an HTTP server, this recursive blocking loop can degrade throughput and waste CPU cycles.
The Role of AsyncEnvironment
To protect production environments from silent performance degradation, you can use the AsyncEnvironment class to configure the strictness of the runtime:
use Hibla\Async\AsyncEnvironment; // Activating strict validation (Highly recommended for high-concurrency daemons) AsyncEnvironment::enableStrictAwait(); // Restoring the cooperative blocking fallback (The default setting) AsyncEnvironment::disableStrictAwait();
Behavioral Differences
| Mode | Outside a Fiber Context ({main}) |
Best Used For |
|---|---|---|
| Flexible Mode (Default) | await() falls back to a cooperative blocking loop and returns synchronously. |
Prototyping, CLI tools, cron scripts, unit tests. |
| Strict Mode | await() instantly throws an InvalidContextException pointing to the file and line. |
Production daemons, HTTP servers, background workers. |
How Strict Mode Protects You (With Detailed Stacktraces)
If strict mode is enabled (for example, automatically turned on by HttpServer::start()), any invalid context boundary crossing is detected immediately.
The resulting InvalidContextException automatically parses the engine stacktrace, bypasses vendor directory frames, and pinpoints the exact file and line in your application code where the invalid await() was called:
Fatal error: Uncaught Hibla\Async\Exceptions\InvalidContextException:
Cannot call await() outside of an active Fiber context.
Maybe this code is using await() under the hood?
Location: app/Http/Controllers/UserController.php:42
Problem: Calling await() outside of a Fiber when strict mode is active forces a synchronous, blocking wait.
Solution: Wrap this call or your callback in async() or asyncFn().
Stack trace:
#0 [internal function]: Hibla\await(...)
#1 app/Http/Controllers/UserController.php(42): Promise::map(...)
...
This ensures that any structural async bugs are discovered loudly and resolved instantly during testing, long before hitting production.
Cancellation inside async()
Pass a CancellationToken to await() calls inside async() blocks to support external cancellation of the entire workflow. When the token is cancelled, the current await() throws CancelledException and the Fiber unwinds naturally through any catch or finally blocks.
Use finally inside async() to guarantee cleanup runs whether the workflow completes normally, throws, or is cancelled:
use Hibla\Cancellation\CancellationTokenSource; use function Hibla\async; use function Hibla\await; $cts = new CancellationTokenSource(); $workflow = async(function () use ($cts) { $connection = openConnection(); try { $user = await(fetchUser(1), $cts->token); $orders = await(fetchOrders($user->id), $cts->token); $report = await(generateReport($user, $orders), $cts->token); return $report; } catch (\Hibla\Promise\Exceptions\CancelledException $e) { echo "Workflow cancelled\n"; return null; } finally { // Always runs on normal completion, exception, or cancellation $connection->close(); } }); // Cancel from anywhere, causing the next await() in the workflow to throw Loop::addTimer(2.0, fn() => $cts->cancel()); $result = await($workflow);
If the token is already cancelled before the first await() inside the Fiber runs, the first await() call throws CancelledException immediately without suspending.
Automatic resource cleanup without track()
When you pass a token to await(), the promise is automatically tracked by the token, so you do not need to call token->track($promise) manually. This is particularly useful when awaiting promises that already have onCancel() handlers registered internally, such as HTTP requests from hiblaphp/http-client. The token triggers the promise's own onCancel() cleanup without any extra wiring at the call site:
$cts = new CancellationTokenSource(5.0); $workflow = async(function () use ($cts) { // Http::get() has an onCancel() handler that aborts the curl request. // Passing $cts->token to await() is enough, so no manual track() call is needed. $response = await(Http::get('https://api.example.com/users'), $cts->token); $data = await(Http::get('https://api.example.com/orders'), $cts->token); return compact('response', 'data'); });
Passing the token directly to await() is the preferred pattern inside async() blocks. It is more concise and keeps the cancellation wiring at the await() call site where the suspension happens.
Combining with Promise Combinators
async() returns a standard Promise so it composes naturally with all of hiblaphp/promise's collection and concurrency methods.
Running tasks concurrently with Promise::all()
[$users, $products, $stats] = await(Promise::all([ async(fn() => fetchUsers()), async(fn() => fetchProducts()), async(fn() => fetchStats()), ]));
Concurrency limiting with Promise::concurrent()
$results = await(Promise::concurrent( array_map( fn($id) => fn() => async(function () use ($id) { $user = await(fetchUser($id)); $orders = await(fetchOrders($user->id)); return compact('user', 'orders'); }), range(1, 100) ), concurrency: 10 ));
Racing with Promise::race()
$fastest = await(Promise::race([ async(fn() => fetchFromRegionA()), async(fn() => fetchFromRegionB()), async(fn() => fetchFromRegionC()), ]));
Timeout with Promise::timeout()
$cts = new CancellationTokenSource(); try { $result = await(Promise::timeout( async(function () use ($cts) { return await(slowOperation(), $cts->token); }), seconds: 5.0 )); } catch (\Hibla\Promise\Exceptions\TimeoutException $e) { echo "Operation timed out\n"; }
Testing Async Code
Because await() holds the script at that line and drives the event loop when called outside a Fiber, you can test async code directly without any special test runner setup, event loop runner, or test helpers. Just call await() at the test level and it drives the loop until the promise settles:
public function test_fetch_user(): void { $user = await(fetchUser(1)); $this->assertEquals('John', $user->name); } public function test_concurrent_fetch(): void { [$user, $orders] = await(Promise::all([ fetchUser(1), fetchOrders(1), ])); $this->assertNotEmpty($orders); } public function test_cancellation(): void { $cts = new CancellationTokenSource(); $cts->cancel(); $this->expectException(\Hibla\Promise\Exceptions\CancelledException::class); await(fetchUser(1), $cts->token); }
This is one of the strongest practical advantages of context-independent await(). The same code that runs non-blocking in production runs with the event loop driven synchronously in tests, with no adaptation required.
Comparison with JavaScript async/await
| JavaScript | Hibla | |
|---|---|---|
await usable in sync functions |
No, syntax error | Yes, holds script while event loop keeps running |
| Function coloring | Yes, spreads upward | No, color lives at call site |
| Marking a function async | Required (async function) |
Not required |
| Return type change | Yes, always returns Promise |
No, return type unchanged |
| Concurrency primitive | async function |
async(fn() => ...) at call site |
| Already-settled promise | Returns on next microtask | Returns immediately, no suspension |
| Context detection | Not available | inFiber() |
| Testing async code | Requires async test runner | Plain await(), event loop runs underneath, no setup |
API Reference
| Function / Static Method | Return Type / Signature | Description |
|---|---|---|
async(callable $function) |
PromiseInterface |
Wrap a callable in a Fiber and schedule it on the event loop. Returns a Promise that resolves with the callable's return value. The callable does not run immediately. It is queued in the next Fiber phase. |
await(PromiseInterface $promise, ?CancellationToken $token) |
mixed |
Inside a Fiber: suspends cooperatively until the promise settles. Outside a Fiber: holds the script at that line and drives the event loop until the promise settles. Returns immediately without suspending for already-settled promises. |
asyncFn(callable $function) |
callable |
Wrap a callable so every call runs inside async() and returns a Promise. Creates a new Fiber per call. |
sleep(float $seconds) |
void |
Inside a Fiber: suspends cooperatively. Outside a Fiber: holds the script at that line while the event loop keeps running underneath. Accepts fractional seconds. |
inFiber() |
bool |
Returns true if currently executing inside a PHP Fiber. |
AsyncEnvironment::enableStrictAwait() |
void |
Enable strict validation mode globally. Calling await() outside an active Fiber context will immediately throw InvalidContextException pinpointing the offending call. |
AsyncEnvironment::disableStrictAwait() |
void |
Disable strict validation mode globally. Calling await() outside an active Fiber context safely falls back to a cooperative blocking event loop. |
AsyncEnvironment::isStrictAwaitEnabled() |
bool |
Check if the active asynchronous environment is currently enforcing strict context validation. |
Development
git clone https://github.com/hiblaphp/async.git
cd async
composer install
./vendor/bin/pest
./vendor/bin/phpstan analyse
Credits
- API Design: Inspired by JavaScript's
async/awaitsyntax and the function coloring problem described by Bob Nystrom in "What Color is Your Function?". Hibla's context-independentawait()is a direct solution to the problem that article described. - Fiber Scheduling: Powered by hiblaphp/event-loop.
- Promise Integration: Built on hiblaphp/promise.
- Cancellation: Powered by hiblaphp/cancellation.
License
MIT License. See LICENSE for more information.
hiblaphp/async 适用场景与选型建议
hiblaphp/async 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.62k 次下载、GitHub Stars 达 6, 最近一次更新时间为 2025 年 09 月 06 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 hiblaphp/async 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 hiblaphp/async 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 2.62k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 6
- 点击次数: 0
- 依赖项目数: 10
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-09-06