PHPackages                             ajayvohra2005/hack-promises - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. ajayvohra2005/hack-promises

ActiveLibrary[Queues &amp; Workers](/categories/queues)

ajayvohra2005/hack-promises
===========================

Promises/A+ implementation in Hack language

v1.0.0(4y ago)0101MITHack

Since Oct 16Pushed 4y agoCompare

[ Source](https://github.com/ajayvohra2005/hack-promises)[ Packagist](https://packagist.org/packages/ajayvohra2005/hack-promises)[ Docs](https://github.com/ajayvohra2005/hack-promises.git)[ RSS](/packages/ajayvohra2005-hack-promises/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (5)Versions (2)Used By (1)

Hack Promises
=============

[](#hack-promises)

This project provides [Promises/A+](https://promisesaplus.com/) implementation in [Hack](https://docs.hhvm.com/hack/).

Overview
========

[](#overview)

Key features
------------

[](#key-features)

- Promises have synchronous `wait` and `cancel` methods.
- Works with any object that implements `HackPromises\ThenableInterface`
- Support for Hack [`HH\KeyedIterator`](https://docs.hhvm.com/hack/reference/interface/HH.KeyedIterator/), and Hack [Generator](https://docs.hhvm.com/hack/asynchronous-operations/generators).

Requirements
------------

[](#requirements)

HHVM 4.128 and above.

Installation
------------

[](#installation)

- Git clone this repository
- Install [composer](https://getcomposer.org/)
- In the root directory of this repository, run the command

    ```
      composer install

    ```

To use this package,

```
    composer require ajayvohra2005/hack-promises

```

Running tests
-------------

[](#running-tests)

After installation, run the following command in the root directory of this repository:

```
    ./vendor/bin/hacktest tests/

```

License
-------

[](#license)

This project is made available under the MIT License (MIT). Please see LICENSE file in this project for more information.

Tutorial
========

[](#tutorial)

A *promise* represents the result of the resolution of an asynchronous operation, and the side-effects associated with the resolution.

Resolving a promise
-------------------

[](#resolving-a-promise)

*Resolving* a promise means that a promise is either *fulfilled* with a *value* or *rejected* with a *reason*. Resolving a promises triggers callbacks registered with the promises's `then` method. These callbacks are triggered only once and in the order in which they were added. When a callback is triggered, it is added to a *global task queue*. When the global task queue is `run`, the tasks on the queue are removed and executed in the order they were added to the queue.

Global task queue
-----------------

[](#global-task-queue)

The global task queue can be `run` as needed, or in an event loop. By default, the global task queue is run *implicitly* once prior to the program shutdown. The global task queue can be run *explictily* as shown below:

```
  HackPromises\TaskQueue::globalTaskQueue()->run();

```

Callbacks
---------

[](#callbacks)

Callbacks are registered with a promise by calling the `then` method of the promise, and by providing optional `$onFulfilled` and `$onRejected` functions, whereby the functions must be of type `HackPromises\ThenCallback`.

When you register callbacks with a promise by calling the `then` method, it always returns a new promise. This can be used to create a chain of promises. The next promise in the chain is invoked with the resolved value of the previous promise in the chain. A promise in the chain is fulfilled only when the previous promise has been fulfilled.

Quick example
-------------

[](#quick-example)

Below we show a quick start example that illustrates the concepts discussed so far:

```
use namespace HackPromises;
use type HackPromises\Promise;

function quick_start_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $promise = new Promise();

  $promise
    ->then((mixed $value): mixed ==> {
        // Return a value and don't break the chain
        return "Hello, " . ($value as string);
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then((mixed $value): void ==> {
        $msg = $value as string;
        echo "{$msg}\n";
    });

  // Resolving the promise triggers the callback, and outputs 'Hello, reader.'.
  // The callbacks are executed on the global task queue, prior to program shutdown
  $promise->resolve('reader.');

}

```

Promise rejection
-----------------

[](#promise-rejection)

When a promise is rejected, the `$onRejected` callbacks are invoked with the rejection reason, as shown in the example below:

```
use type HackPromises\Promise;

function promise_rejection_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

    $promise = new Promise();
    $promise
        ->then(null, (mixed $reason): void ==> {
            $msg = $reason as string;
            echo "{$msg}\n";
        });

    // Outputs "Error!"
    $promise->reject('Error!');
}

```

Rejection forwarding
--------------------

[](#rejection-forwarding)

If an exception is thrown in an `$onRejected` callback, subsequent `$onRejected` callbacks are invoked with the thrown exception as the reason.

```
use type HackPromises\{Promise, RejectedPromise};

function rejection_forwarding_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

    $promise = new Promise();
    $promise
        ->then(null, (mixed $reason): mixed ==> {
            return new RejectedPromise($reason);
        })
        ->then(null, (mixed $reason): void ==> {
        });

    // Outputs nothing
    $promise->reject('No Error!');
}

```

If an exception is not thrown in a `$onRejected` callback and the callback does not return a rejected promise, downstream `$onFulfilled` callbacks are invoked using the value returned from the `$onRejected` callback, as shown in the example below:

```
use namespace HackPromises as P;

function ignore_rejection_example(): void
{
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $promise = new P\Promise();
  $promise ->then(null, (mixed $reason): mixed ==> {
        return "It's ok";
    })->then( (mixed $value): void ==> {
        echo $value as string;
    });

  // Outputs 'It's ok'
  $promise->reject('Error!');
}

```

Synchronous wait
----------------

[](#synchronous-wait)

You can resolve a promise synchronously by caling the `wait` method on a promise.

Calling the `wait` method of a promise invokes the *wait* function provided to the promise, and implicitly runs the global task queue. An example showing the use of the `wait` method is shown below:

```
use namespace HackPromises;

use type HackPromises\Promise;
use type HackPromises\ResolveCallback;

function promise_wait_function_example(): void {
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $wait_fn = (ResolveCallback $cb) ==> {
    $cb('reader.');
  };

  $promise = new Promise($wait_fn);

  $promise
    ->then((mixed $value): mixed ==> {
        // Return a value and don't break the chain
        return "Hello, " . ($value as string);
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then((mixed $value): void ==> {
        $msg = $value as string;
        echo "{$msg}\n";
    });

  // Calling wait  resolves promise synchronously and outputs 'Hello, reader.'.
  $promise->wait();
}

```

If an exception is encountered while invoking the *wait* function of a promise, the promise is rejected with the exception and the exception is thrown. Calling `wait` method of a promise that has been fulfilled will not trigger the *wait* function. It will simply return the previously resolved value

Calling `wait` method on a promise that has been rejected will throw an exception. If the rejection reason is an instance of `\Exception` the *reason* is thrown. Otherwise, a `HackPromises\RejectionException` is thrown and the *reason* can be obtained by calling the `getReason` method of the exception.

### Unwrapping a promise

[](#unwrapping-a-promise)

When you call the `wait` method without an argument, or with the argument `true`, it unwraps the promiose. Unwrapping the promise returns the value of the promise if it was *fulfilled*, or throws an exception if the promise was *rejected*. You can force a promise to resolve but *not* unwrap by passing `false` to the `wait` method of the promise, as shown in the example below:

```
$promise = new Promise();
$promise->reject('foo');
// This will not throw an exception. It simply ensures the promise has
// been resolved.
$promise->wait(false);

```

When unwrapping a promise, the resolved value of the promise will be waited upon until the unwrapped value is not a promise. This means that if you resolve promise A with a promise B and unwrap promise A, the value returned by the wait function will be the result of resolving promise B, as shown in the example below;

```
use namespace HackPromises as P;

function unwrapping_example(): void
{
  require_once(__DIR__.'/../vendor/autoload.hack');
  \Facebook\AutoloadMap\initialize();

  $b = new P\Promise();
  $a = new P\Promise( (P\ResolveCallback $cb): void ==> { $cb($b);});
  $b->resolve('foo');
  $result = $a->wait() as string;

  // Outputs 'foo'
  echo $result;
}

```

Synchronous cancel
------------------

[](#synchronous-cancel)

You can cancel a promise that has not yet been fulfilled synchronously using the `cancel()`method of a promise.

API classes
===========

[](#api-classes)

Promise
-------

[](#promise)

For the base case, you can use `Promise` class to create a promise.

If you want to resolve the promise **asynchronosuly** and invoke the registerd `then` callbacks, you can create the `Promise` without any arguments to the constructor.

If you want be able to **synchronously** resolve or cancel the `Promise` , pass the *wait* and *cancel* functions to the constructor.

### Wait function

[](#wait-function)

The *wait* function must be of type `HackPromises\WaitFunction`. The *wait* function provided to a `Promise` constructor is invoked when the `wait` method of the `Promise` is called. The *wait* function must resolve the `Promise` by calling the `HackPromises\ResolveCallback` function passed to it as an argument, or throw an exception.

### Cancel function

[](#cancel-function)

When creating a `Promise` you can provide an optional *cancel* function of type `HackPromises\CancelFunction`, which is invoked by the promise if you call the `cancel()` method of the promise. The *cancel* function may optionally reject the promise by invoking the `HashPromises\RejectCallback` function passed to it as an argument.

FulfilledPromise
----------------

[](#fulfilledpromise)

The `FulfilledPromise` class object is fulfilled on construction.

RejectedPromise
---------------

[](#rejectedpromise)

The `RejectedPromise` class object is rejected on construction.

IteratorPromise
---------------

[](#iteratorpromise)

The `IteratorPromise` class creates an aggregated promise that iterates over an iterator of promises, or values, and triggers callback functions in the process. Use `Create::iterFor` to create an iterator for a `vec` or a `dict`.

GeneratorPromise
----------------

[](#generatorpromise)

The `GeneratorPromise` class wraps a generator that yields values, or promises.

Create
------

[](#create)

The `Create` class provides helper methods.

Acknowledgements
================

[](#acknowledgements)

This project is inspired by [Guzzle Promises](https://github.com/guzzle/promises). However, it has signficant differences in API and implementation, owing to the language and best-practices requirements of Hack.

###  Health Score

23

—

LowBetter than 26% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1721d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cfef5eb52d2742e88bd534ddfd42add3b7e89dc72bd4ada74e2a8d0e0b335ac0?d=identicon)[ajayvohra2005](/maintainers/ajayvohra2005)

---

Top Contributors

[![ajayvohra2005](https://avatars.githubusercontent.com/u/3870355?v=4)](https://github.com/ajayvohra2005 "ajayvohra2005 (1 commits)")

---

Tags

promiseshackhacklang

### Embed Badge

![Health badge](/badges/ajayvohra2005-hack-promises/health.svg)

```
[![Health](https://phpackages.com/badges/ajayvohra2005-hack-promises/health.svg)](https://phpackages.com/packages/ajayvohra2005-hack-promises)
```

###  Alternatives

[react/promise

A lightweight implementation of CommonJS Promises/A for PHP

2.5k387.6M643](/packages/react-promise)[internal/promise

A lightweight implementation of CommonJS Promises/A for PHP

541.3M3](/packages/internal-promise)[quizlet/hammock

Hammock is a stand-alone mocking library for Hacklang.

27445.5k](/packages/quizlet-hammock)[hackpack/hackunit

An xUnit testing framework for Hack

612.7k13](/packages/hackpack-hackunit)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
