Hello, and welcome back to doPHP.dev!
High-throughput backend engineering demands non-blocking I/O. By leveraging modern fiber-based runtimes, PHP applications are achieving Go-tier concurrency natively. If you are building complex systems that rely on external data, the days of waiting sequentially for network responses are officially behind us.
By leveraging native Fibers alongside application servers like RoadRunner or Swoole, PHP can now operate as an event-driven, long-running process capable of high-throughput asynchronous tasks. Today, we are going to dive into how you can use Fibers and asynchronous task runners to make concurrent HTTP requests, completely unblocking your application's execution path.
In a legacy, synchronous execution model, operations happen one at a time. If your application needs to fetch user data from a CRM API, fetch a billing invoice from Stripe, and grab a geolocation payload from a third service, a traditional PHP script will execute these requests in order.
If API A takes 2 seconds, API B takes 1 second, and API C takes 2 seconds, your total execution time is 5 seconds. The CPU isn't actually working hard during this time; it's just sitting idly, waiting for the network to respond.
Instead of sitting idle and blocking execution while fetching data from a slow external API, your persistent PHP workers can juggle multiple tasks concurrently.
Introduced natively in PHP 8.1, Fibers are the foundation of cooperative multitasking. They allow a function's execution to be paused, completely yielding control back to the main event loop, and then resumed later right where it left off.
When paired with a modern asynchronous event loop (like Revolt) and concurrent HTTP clients, Fibers allow us to dispatch all of our network requests simultaneously.
Let's look at how this paradigm shift actually translates into code. In this example, we will use a Fiber-aware asynchronous HTTP client to request data from three separate endpoints at the exact same time.
PHP
1<?php 2 3declare(strict_types=1); 4 5use Amp\Http\Client\HttpClientBuilder; 6use Amp\Http\Client\Request; 7use function Amp\Future\awaitAll; 8use function Amp\async; 9 10require __DIR__ . '/vendor/autoload.php';11 12// Initialize a Fiber-aware HTTP client13$client = HttpClientBuilder::buildDefault();14 15$endpoints = [16 'https://api.example.com/crm/users/1',17 'https://api.example.com/billing/invoices/99',18 'https://api.example.com/geo/location/US',19];20 21echo "Dispatching API calls concurrently...\n";22$startTime = microtime(true);23 24// We use async() to map each URL to an asynchronous network request.25// This dispatches the requests simultaneously without blocking the loop.26$promises = array_map(function (string $url) use ($client) {27 return async(function () use ($client, $url) {28 $response = $client->request(new Request($url));29 return $response->getBody()->buffer();30 });31}, $endpoints);32 33// awaitAll() pauses the current execution context via Fibers34// until ALL concurrent requests have resolved.35$responses = awaitAll($promises);36 37$endTime = microtime(true);38$executionTime = round($endTime - $startTime, 2);39 40echo "All tasks completed in {$executionTime} seconds!\n";
How It Works Under the Hood:
async(): Rather than executing the HTTP request immediately and blocking, async() spins up a new execution context. It fires off the network request and immediately yields control back to the engine.
Non-Blocking I/O: Because the actual network stream reading is non-blocking, the engine can fire off the second and third requests while still waiting for the first one to return.
awaitAll(): This is where the Fiber magic happens. The engine suspends the main execution scope, waiting for all three asynchronous promises to resolve. Once the last API responds, the Fiber is resumed, and your data is handed back for processing.
If API A takes 2 seconds, API B takes 1 second, and API C takes 2 seconds, your total execution time in this model is now 2 seconds (the time of the longest single request), rather than 5 seconds.
Moving to an event-driven model isn't just about speed; it's about architectural efficiency.
When you eliminate idle waiting, your application can handle exponentially more traffic using the exact same hardware footprint. Your servers are spending their CPU cycles executing domain logic rather than waiting for third-party network stacks to respond. This is the cornerstone of modern, resilient backend architecture.
Happy coding, and welcome to the asynchronous future of PHP!
About the author
Darren Odden is a seasoned software developer and web architect specializing in the modern PHP and Laravel ecosystems, where he designs elegant APIs and robust web applications. As a dedicated tech advocate, he focuses on community building and championing clean, modern development practices. When he isn’t diving into code or fine-tuning tech stacks, Darren balances his digital life by hitting the open road with his family in their travel trailer, camping, solving crossword puzzles, and immersing himself in the rich subcultures of classic hip-hop and vintage graffiti art.
Built for Developers, by Developers
Join the movement and discover why modern PHP is the sophisticated choice for elegant, high-scale applications in 2026.
Platform
Reach Us
Santa Cruz, CA 95062
©2026 doPHP.dev
Proudly built with PHP 8.3 (running on 8.5), Laravel 13, and Statamic 6. How it’s built.