-
-
Notifications
You must be signed in to change notification settings - Fork 3
Home
In computer science, a Promise
is a construct that bridges the gap between procedural code and asynchronous execution. In procedural languages like PHP, functions influence program flow by either returning a value or throwing an exception, but when dealing with asynchronous operations, we can't return a value (because it isn't ready yet), and we can't throw an exception (because there's no clear place to catch it).
This is where Promises come in. Instead of returning a value or throwing an exception, an asynchronous function returns a Promise
: an object that will eventually be settled by fulfilling with a value or rejecting with an error — but not immediately.
In this library, that background work is encapsulated by the Deferred
class, which allows execution to be deferred while still exposing a Promise
object that can be resolved or rejected later.
This implementation follows the [Web API's Promise specification]mdn-promise in terms of syntax and expected behaviour — using then
, catch
, and finally
— but is tailored to work natively in PHP.
Note
While the API is compatible with Promises/A+, this library differs in one important way: instead of returning a new Promise with each then
, catch
, or finally
call, it maintains and returns the same instance throughout the chain. This design keeps memory usage lower, avoids deep call stacks, and results in simpler, more predictable code — especially helpful in procedural environments like PHP.
A basic usage example:
$exampleSlowFileReader->read()
->then(function(string $contents) {
echo "Contents of file: $contents", PHP_EOL;
});
A more advanced example demonstrating chaining:
$exampleRemoteApi->getCustomerById(105)
->then(function(Customer $customer) {
return $customer->loadLatestOrders(new DateTime("-5 weeks"));
})
->then(function(CustomerOrderList $orders) {
echo "Customer {$orders->getCustomer()->getName()} ",
"has made ", count($orders), " orders in the last 5 weeks!", PHP_EOL;
})
->catch(function(Throwable $reason) {
echo "Error loading customer data: $reason", PHP_EOL;
})
->finally(function() use($exampleRemoteApi) {
$exampleRemoteApi->disconnect();
});
This library separates responsibility between Deferred
and Promise
objects:
- The
Deferred
handles how and when a task runs. - The
Promise
represents the result of that task and is returned immediately.
To integrate this model into a class:
class Example {
private Deferred $deferred;
public function doTheTask():Promise {
$this->deferred = new Deferred(fn() => $this->processFunction());
return $this->deferred->getPromise();
}
private function processFunction():void {
if($this->isThereMoreWorkToDo()) {
$this->doMoreWork();
}
else {
$this->deferred->resolve($this->getFinalValue());
}
}
private function isThereMoreWorkToDo():bool { /* ... */ }
private function doMoreWork():void { /* ... */ }
private function getFinalValue() { /* ... */ }
}
This can then be consumed like this:
$example = new Example();
$example->doTheTask()
->then(function($finalValue) {
echo "Final value: ", $finalValue;
});
The caller doesn’t need to know anything about Deferred
or Promise
; it just uses a public method that returns a PromiseInterface
.
PHP is a procedural language — it won’t call your Deferred
's processing function automatically. For your promises to resolve, you’ll need an event loop to run the pending processes.
This could be a simple loop like:
while($runner->hasPendingTasks()) {
$runner->tick();
}
For production use, consider using a solution built for looping PHP. PHP.GT/async is the complementary library for handling asynchronous looping, which is built to coordinate these tasks efficiently in real-world PHP environments.
PHP.GT/Promise is a separately maintained component of PHP.GT/WebEngine.