PHPackages                             sof3/await-generator - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. sof3/await-generator

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

sof3/await-generator
====================

Use async/await in PHP using generators

3.6.1(2y ago)12424.6k↑181.3%18[6 issues](https://github.com/SOF3/await-generator/issues)[3 PRs](https://github.com/SOF3/await-generator/pulls)19apache-2.0PHPPHP ^8.0CI failing

Since Jun 29Pushed 4d ago5 watchersCompare

[ Source](https://github.com/SOF3/await-generator)[ Packagist](https://packagist.org/packages/sof3/await-generator)[ RSS](/packages/sof3-await-generator/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependencies (4)Versions (30)Used By (19)

Eng | [繁](zho) | [简](chs)

await-generator
===============

[](#await-generator)

[![Build Status](https://github.com/SOF3/await-generator/workflows/CI/badge.svg)](https://github.com/SOF3/await-generator/actions?query=workflow%3ACI)[![Codecov](https://camo.githubusercontent.com/def8f878f6a67ecc0095afcbc1be0ec3f94c1be4191521c6f4e8337ebbd7bc5e/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f636f6465636f762f6578616d706c652d707974686f6e2e737667)](https://codecov.io/gh/SOF3/await-generator)

A library to use async/await pattern in PHP.

Documentation
-------------

[](#documentation)

Read the [await-generator tutorial](https://sof3.github.io/await-generator/master/) for an introduction from generators and traditional async callbacks to await-generator.

Why await-generator?
--------------------

[](#why-await-generator)

Traditional async programming requires callbacks, which leads to spaghetti code known as "callback hell":

 Click to reveal example callback hell```
load_data(function($data) {
    $init = count($data) === 0 ? init_data(...) : fn($then) => $then($data);
    $init(function($data) {
        $output = [];
        foreach($data as $k => $datum) {
            processData($datum, function($result) use(&$output, $data) {
                $output[$k] = $result;
                if(count($output) === count($data)) {
                    createQueries($output, function($queries) {
                        $run = function($i) use($queries, &$run) {
                            runQuery($queries[$i], function() use($i, $queries, $run) {
                                if($i === count($queries)) {
                                    $done = false;
                                    commitBatch(function() use(&$done) {
                                        if(!$done) {
                                            $done = true;
                                            echo "Done!\n";
                                        }
                                    });
                                    onUserClose(function() use(&$done) {
                                        if(!$done) {
                                            $done = true;
                                            echo "User closed!\n";
                                        }
                                    });
                                    onTimeout(function() use(&$done) {
                                        if(!$done) {
                                            $done = true;
                                            echo "Timeout!\n";
                                        }
                                    });
                                } else {
                                    $run($i + 1);
                                }
                            });
                        };
                    });
                }
            });
        }
    });
});
```

With await-generator, this is simplified into: ```
$data = yield from load_data();
if(count($data) === 0) $data = yield from init_data();
$output = yield from Await::all(array_map(fn($datum) => processData($datum), $data));
$queries = yield from createQueries($output);
foreach($queries as $query) yield from runQuery($query);
[$which, ] = yield from Await::race([
    0 => commitBatch(),
    1 => onUserClose(),
    2 => onTimeout(),
])
echo match($which) {
    0 => "Done!\n",
    1 => "User closed!\n",
    2 => "Timeout!\n",
};
```

Can I maintain backward compatibility?
--------------------------------------

[](#can-i-maintain-backward-compatibility)

Yes, await-generator does not impose any restrictions on your existing API. You can wrap all await-generator calls as internal implementation detail, although you are strongly encouraged to expose the generator functions directly.

await-generator starts an await context with the `Await::f2c` method, with which you can adapt into the usual callback syntax:

```
function oldApi($args, Closure $onSuccess) {
    Await::f2c(fn() => $onSuccess(yield from newApi($args)));
}
```

Or if you want to handle errors too:

```
function newApi($args, Closure $onSuccess, Closure $onError) {
    Await::f2c(function() use($onSuccess, $onError) {
        try {
            $onSuccess(yield from newApi($args));
        } catch(Exception $ex) {
            $onError($ex);
        }
    });
}
```

You can continue to call functions implemented as callback style using the `Await::promise` method (similar to `new Promise` in JS):

```
yield from Await::promise(fn($resolve, $reject) => oldFunction($args, $resolve, $reject));
```

Why *not* await-generator
-------------------------

[](#why-not-await-generator)

await-generator has a few common pitfalls:

- Forgetting to `yield from` a `Generator` method will end up doing nothing.
- If you delete all `yield`s from a function, it automatically becomes a non-generator function thanks to PHP magic. This issue can be mitigated by always adding `: Generator` to the function signature.
- `finally` blocks may never get executed if an async function never resolves (e.g. `Await::promise(fn($resolve) => null)`).

While these pitfalls cause some trouble, await-generator style is still much less bug-prone than a callback hell.

But what about fibers?
----------------------

[](#but-what-about-fibers)

This might be a subjective comment, but I do not prefer fibers for a few reasons:

### Explicit suspension in type signature

[](#explicit-suspension-in-type-signature)

[![fiber.jpg](./fiber.jpeg)](./fiber.jpeg)

For example, it is easy to tell from the type signature that `$channel->send($value): Generator` suspends until the value is sent and `$channel->sendBuffered($value): void`is a non-suspending method that returns immediately. Type signatures are often self-explanatory.

Of course, users could call `sleep()` anyway, but it is quite obvious to everyone that `sleep()` blocks the whole runtime (if they didn't already know, they will find out when the whole world stops).

### Concurrent states

[](#concurrent-states)

When a function suspends, many other things can happen. Indeed, calling a function allows the implementation to call any other functions which could modify your states anyway, but a sane, genuine implementation of e.g. an HTTP request wouldn't call functions that modify the private states of your library. But this assumption does not hold with fibers because the fiber is preempted and other fibers can still modify the private states. This means you have to check for possible changes in private properties every time you call any function that *might* be suspending.

On the other hand, using explicit await, it is obvious where exactly the suspension points are, and you only need to check for state mutations at the known suspension points.

### Trapping suspension points

[](#trapping-suspension-points)

await-generator provides a feature called ["trapping"](https://github.com/SOF3/await-generator/pull/106), which allows users to add pre-suspend and pre-resume hooks to a generator. This is simply achieved by adding an adapter to the generator, and does not even require explicit support from the await-generator runtime. This is currently not possible with fibers.

###  Health Score

58

—

FairBetter than 98% of packages

Maintenance64

Regular maintenance activity

Popularity43

Moderate usage in the ecosystem

Community33

Small or concentrated contributor base

Maturity79

Established project with proven stability

 Bus Factor1

Top contributor holds 54% 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

Every ~103 days

Recently: every ~27 days

Total

19

Last Release

1072d ago

Major Versions

1.0.0 → 2.0.02018-08-18

2.3.0 → 3.0.02020-06-11

PHP version history (4 changes)2.0.0PHP ^7.2

2.3.0PHP ^7.3

3.1.0PHP ^7.3 || ^8.0

3.4.0PHP ^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2801890?v=4)[SOFe](/maintainers/SOFe)[@sofe](https://github.com/sofe)

---

Top Contributors

[![SOF3](https://avatars.githubusercontent.com/u/19623715?v=4)](https://github.com/SOF3 "SOF3 (191 commits)")[![dependabot-preview[bot]](https://avatars.githubusercontent.com/in/2141?v=4)](https://github.com/dependabot-preview[bot] "dependabot-preview[bot] (113 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (19 commits)")[![Endermanbugzjfc](https://avatars.githubusercontent.com/u/53002741?v=4)](https://github.com/Endermanbugzjfc "Endermanbugzjfc (13 commits)")[![fuyutsuki](https://avatars.githubusercontent.com/u/16377174?v=4)](https://github.com/fuyutsuki "fuyutsuki (10 commits)")[![JavierLeon9966](https://avatars.githubusercontent.com/u/58715544?v=4)](https://github.com/JavierLeon9966 "JavierLeon9966 (3 commits)")[![Frago9876543210](https://avatars.githubusercontent.com/u/13465245?v=4)](https://github.com/Frago9876543210 "Frago9876543210 (1 commits)")[![Encritary](https://avatars.githubusercontent.com/u/6199266?v=4)](https://github.com/Encritary "Encritary (1 commits)")[![aieuo](https://avatars.githubusercontent.com/u/35859665?v=4)](https://github.com/aieuo "aieuo (1 commits)")[![kostamax27](https://avatars.githubusercontent.com/u/69608272?v=4)](https://github.com/kostamax27 "kostamax27 (1 commits)")[![Laith98Dev](https://avatars.githubusercontent.com/u/49840784?v=4)](https://github.com/Laith98Dev "Laith98Dev (1 commits)")

---

Tags

async-awaitasync-generatorawaitphpphp-asyncvirion

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sof3-await-generator/health.svg)

```
[![Health](https://phpackages.com/badges/sof3-await-generator/health.svg)](https://phpackages.com/packages/sof3-await-generator)
```

###  Alternatives

[getkirby/geo

Kirby Geo

223.9k](/packages/getkirby-geo)

PHPackages © 2026

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