Get "PHP 8 in a Nuthshell" (Now with PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Using JavaScript-style Promises in PHP

If you’re familiar with JavaScript, you might have worked with or at least heard about Promises at some point of time.

So, in a nutshell (in the context of JavaScript),

A promise is an object that may produce a single value sometime in the future: 1. a resolved value, or 2. a reason that it’s not resolved (e.g., a network error occurred)

Based on this, a promise can be in one of these 3 possible states:

  1. Pending
  2. Fulfiled
  3. Rejected

A Promise in JavaScript can be created like so.

let promise = new Promise(function(resolve, reject) {
  // do something
});

As you can see, the Promise object here accepts a callback. The callback function then takes two arguments: resolve and reject. The arguments resolve and reject are callbacks provided by JavaScript itself.

Both of these callbacks can be called inside the promise callback in the following scenario.

  • resolve(value) — If the job finished successfully, with result value.
  • reject(error) — If an error occurred, error is the error object.

So, if we want to create a promise based on these, it would be like so.

let promise = new Promise(function(resolve, reject) {
    if(somthingIsTrue) {
        resolve("Promise is resolved");
    } else {
        reject("Whoops!")
    }
});

And this promise than can be consumed using basically three methods: .then, .catch and .finally like so.

promise.then(
    result => alert(result), // Alerts "Promise is resolved"
).catch(
    error => alert(error) // Alerts "Whoops!" if the promise gets rejected
).finally(
    () => alert('Runs even if the promise is resolved or not')
);

Using Promises in PHP? Oh yeah!

Now, that’s about JavaScript! But what if you want to do something like this in PHP? Well, like JavaScript, PHP is also a single-thread language. Meaning, it has one call stack and one memory heap to process things. But unlike JavaScript, PHP doesn’t come with Promises in-built.

But the good news is, good folks at ReactPHP have implemented a Promise implementation based on CommonJS Promises/A standards upon which the JavaScript promises has been implemented.

To get started, you’ll have to implement the library using Composer like so.

$ composer require react/promise:^2.8

That’s it! You can start using the promises right away.

Creating a Promise

Just like JavaScript, we have a Promise object which accepts two callbacks: a $resolver and $canceller (just like JavaScript promises) like so.

$promise = new React\Promise\Promise($resolver, $canceller);

Next, we’ll pass in actual callbacks to the Promise object like so.

include_once './vendor/autoload.php';

use React\Promise\Promise;

$resolver = function (callable $resolve, callable $reject, callable $notify) {
    
    $resolve('Promise is resolved');
    // or throw new Exception('Promise rejected');
    // or $resolve($anotherPromise);
    // or $reject($nastyError);
    // or $notify($progressNotification);
};

$canceller = function () {
    // Cancel/abort any running operations like network connections, streams etc.

    // Reject promise by throwing an exception
    throw new \Exception('Promise cancelled');
};

$promise = new Promise($resolver, $canceller);

Now, as you can see, the $resolver accepts three parameters: $resolve, $reject, and $notify which are all methods which you can call in different circumstances.

  • $resolve can be called when the job of the promise is successful with the result.
  • $reject can be explicitly called when you want to reject the promise in case of any exception or error or you can just throw an exception if you want to reject the promise.
  • $notify can be called when you want to give information about the progression of the promise.

If the resolver or canceller throws an exception, the promise will be rejected with that thrown exception as the rejection reason.

The resolver function will be called immediately, the canceller function only once all consumers called the cancel() method of the promise.

In our example, I’ve used $resovle just to demonstrate how it will return the result when we’ll be “consuming” the promise. Which takes us to the final part of this article.

Consuming a Promise

Once the promise is created, it’s now ready to be consumed.

So, when the promise is fulfilled successfully, we can call then() method on the promise which accepts two callbacks like so. So, using the promise from the previous example.

$promise
    ->then(function($value) {
        echo $value; // prints "Promise is resolved"
    }, function($value) {
        echo $value; // prints "Promise cancelled"
    });

The first callback will be called the promise is successfully “resolved” with a value. And the second callback will be called when the promise is “rejected” with the error.

Oh, and you can also chain the promises if you want to perform additional operations on the promise result like so.

$promise
    ->then(function($value) {
        return strtoupper($value);
    })
    ->then(function($value) {
        echo $value; // prints "PROMISE IS RESOLVED"
    }, function($value) {
        echo $value; // prints "PROMISE CANCELLED"
    });

And that wraps the introduction to get started with promises in PHP, JavaScript-style.

Let me know in the comments what you liked most about this implementation and in which different scenarios you would use Promises.

You can learn about Promises more thoroughly on its official documentation.

Until next time!

Learn the fundamentals of PHP 8 (including 8.1, 8.2, 8.3, and 8.4), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?