{"id":13616452,"url":"https://github.com/reactphp/async","last_synced_at":"2025-05-16T09:03:43.045Z","repository":{"id":3638783,"uuid":"4705850","full_name":"reactphp/async","owner":"reactphp","description":"Async utilities and fibers for ReactPHP.","archived":false,"fork":false,"pushed_at":"2024-08-27T08:46:50.000Z","size":188,"stargazers_count":209,"open_issues_count":5,"forks_count":18,"subscribers_count":15,"default_branch":"4.x","last_synced_at":"2025-05-08T04:01:59.791Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://reactphp.org/async/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["reactphp","clue","WyriHaximus"],"open_collective":"reactphp"}},"created_at":"2012-06-18T20:29:10.000Z","updated_at":"2025-05-03T20:59:35.000Z","dependencies_parsed_at":"2023-09-27T07:45:44.942Z","dependency_job_id":"e793a2cd-f399-49a0-82f6-0d2d12f5bbb9","html_url":"https://github.com/reactphp/async","commit_stats":{"total_commits":84,"total_committers":6,"mean_commits":14.0,"dds":"0.40476190476190477","last_synced_commit":"7c3738e837b38c9513af44398b8c1b2b1be1fbbc"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fasync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fasync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fasync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fasync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactphp","download_url":"https://codeload.github.com/reactphp/async/tar.gz/refs/heads/4.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253284111,"owners_count":21883759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T20:01:28.713Z","updated_at":"2025-05-16T09:03:43.017Z","avatar_url":"https://github.com/reactphp.png","language":"PHP","funding_links":["https://github.com/sponsors/reactphp","https://github.com/sponsors/clue","https://github.com/sponsors/WyriHaximus","https://opencollective.com/reactphp"],"categories":["PHP"],"sub_categories":[],"readme":"# Async Utilities\n\n[![CI status](https://github.com/reactphp/async/workflows/CI/badge.svg)](https://github.com/reactphp/async/actions)\n[![installs on Packagist](https://img.shields.io/packagist/dt/react/async?color=blue\u0026label=installs%20on%20Packagist)](https://packagist.org/packages/react/async)\n\nAsync utilities and fibers for [ReactPHP](https://reactphp.org/).\n\nThis library allows you to manage async control flow. It provides a number of\ncombinators for [Promise](https://github.com/reactphp/promise)-based APIs.\nInstead of nesting or chaining promise callbacks, you can declare them as a\nlist, which is resolved sequentially in an async manner.\nReact/Async will not automagically change blocking code to be async. You need\nto have an actual event loop and non-blocking libraries interacting with that\nevent loop for it to work. As long as you have a Promise-based API that runs in\nan event loop, it can be used with this library.\n\n**Table of Contents**\n\n* [Usage](#usage)\n    * [async()](#async)\n    * [await()](#await)\n    * [coroutine()](#coroutine)\n    * [delay()](#delay)\n    * [parallel()](#parallel)\n    * [series()](#series)\n    * [waterfall()](#waterfall)\n* [Todo](#todo)\n* [Install](#install)\n* [Tests](#tests)\n* [License](#license)\n\n## Usage\n\nThis lightweight library consists only of a few simple functions.\nAll functions reside under the `React\\Async` namespace.\n\nThe below examples refer to all functions with their fully-qualified names like this:\n\n```php\nReact\\Async\\await(…);\n```\n\nAs of PHP 5.6+ you can also import each required function into your code like this:\n\n```php\nuse function React\\Async\\await;\n\nawait(…);\n```\n\nAlternatively, you can also use an import statement similar to this:\n\n```php\nuse React\\Async;\n\nAsync\\await(…);\n```\n\n### async()\n\nThe `async(callable():(PromiseInterface\u003cT\u003e|T) $function): (callable():PromiseInterface\u003cT\u003e)` function can be used to\nreturn an async function for a function that uses [`await()`](#await) internally.\n\nThis function is specifically designed to complement the [`await()` function](#await).\nThe [`await()` function](#await) can be considered *blocking* from the\nperspective of the calling code. You can avoid this blocking behavior by\nwrapping it in an `async()` function call. Everything inside this function\nwill still be blocked, but everything outside this function can be executed\nasynchronously without blocking:\n\n```php\nLoop::addTimer(0.5, React\\Async\\async(function () {\n    echo 'a';\n    React\\Async\\await(React\\Promise\\Timer\\sleep(1.0));\n    echo 'c';\n}));\n\nLoop::addTimer(1.0, function () {\n    echo 'b';\n});\n\n// prints \"a\" at t=0.5s\n// prints \"b\" at t=1.0s\n// prints \"c\" at t=1.5s\n```\n\nSee also the [`await()` function](#await) for more details.\n\nNote that this function only works in tandem with the [`await()` function](#await).\nIn particular, this function does not \"magically\" make any blocking function\nnon-blocking:\n\n```php\nLoop::addTimer(0.5, React\\Async\\async(function () {\n    echo 'a';\n    sleep(1); // broken: using PHP's blocking sleep() for demonstration purposes\n    echo 'c';\n}));\n\nLoop::addTimer(1.0, function () {\n    echo 'b';\n});\n\n// prints \"a\" at t=0.5s\n// prints \"c\" at t=1.5s: Correct timing, but wrong order\n// prints \"b\" at t=1.5s: Triggered too late because it was blocked\n```\n\nAs an alternative, you should always make sure to use this function in tandem\nwith the [`await()` function](#await) and an async API returning a promise\nas shown in the previous example.\n\nThe `async()` function is specifically designed for cases where it is used\nas a callback (such as an event loop timer, event listener, or promise\ncallback). For this reason, it returns a new function wrapping the given\n`$function` instead of directly invoking it and returning its value.\n\n```php\nuse function React\\Async\\async;\n\nLoop::addTimer(1.0, async(function () { … }));\n$connection-\u003eon('close', async(function () { … }));\n$stream-\u003eon('data', async(function ($data) { … }));\n$promise-\u003ethen(async(function (int $result) { … }));\n```\n\nYou can invoke this wrapping function to invoke the given `$function` with\nany arguments given as-is. The function will always return a Promise which\nwill be fulfilled with whatever your `$function` returns. Likewise, it will\nreturn a promise that will be rejected if you throw an `Exception` or\n`Throwable` from your `$function`. This allows you to easily create\nPromise-based functions:\n\n```php\n$promise = React\\Async\\async(function (): int {\n    $browser = new React\\Http\\Browser();\n    $urls = [\n        'https://example.com/alice',\n        'https://example.com/bob'\n    ];\n\n    $bytes = 0;\n    foreach ($urls as $url) {\n        $response = React\\Async\\await($browser-\u003eget($url));\n        assert($response instanceof Psr\\Http\\Message\\ResponseInterface);\n        $bytes += $response-\u003egetBody()-\u003egetSize();\n    }\n    return $bytes;\n})();\n\n$promise-\u003ethen(function (int $bytes) {\n    echo 'Total size: ' . $bytes . PHP_EOL;\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThe previous example uses [`await()`](#await) inside a loop to highlight how\nthis vastly simplifies consuming asynchronous operations. At the same time,\nthis naive example does not leverage concurrent execution, as it will\nessentially \"await\" between each operation. In order to take advantage of\nconcurrent execution within the given `$function`, you can \"await\" multiple\npromises by using a single [`await()`](#await) together with Promise-based\nprimitives like this:\n\n```php\n$promise = React\\Async\\async(function (): int {\n    $browser = new React\\Http\\Browser();\n    $urls = [\n        'https://example.com/alice',\n        'https://example.com/bob'\n    ];\n\n    $promises = [];\n    foreach ($urls as $url) {\n        $promises[] = $browser-\u003eget($url);\n    }\n\n    try {\n        $responses = React\\Async\\await(React\\Promise\\all($promises));\n    } catch (Exception $e) {\n        foreach ($promises as $promise) {\n            $promise-\u003ecancel();\n        }\n        throw $e;\n    }\n\n    $bytes = 0;\n    foreach ($responses as $response) {\n        assert($response instanceof Psr\\Http\\Message\\ResponseInterface);\n        $bytes += $response-\u003egetBody()-\u003egetSize();\n    }\n    return $bytes;\n})();\n\n$promise-\u003ethen(function (int $bytes) {\n    echo 'Total size: ' . $bytes . PHP_EOL;\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThe returned promise is implemented in such a way that it can be cancelled\nwhen it is still pending. Cancelling a pending promise will cancel any awaited\npromises inside that fiber or any nested fibers. As such, the following example\nwill only output `ab` and cancel the pending [`delay()`](#delay).\nThe [`await()`](#await) calls in this example would throw a `RuntimeException`\nfrom the cancelled [`delay()`](#delay) call that bubbles up through the fibers.\n\n```php\n$promise = async(static function (): int {\n    echo 'a';\n    await(async(static function (): void {\n        echo 'b';\n        delay(2);\n        echo 'c';\n    })());\n    echo 'd';\n\n    return time();\n})();\n\n$promise-\u003ecancel();\nawait($promise);\n```\n\n### await()\n\nThe `await(PromiseInterface\u003cT\u003e $promise): T` function can be used to\nblock waiting for the given `$promise` to be fulfilled.\n\n```php\n$result = React\\Async\\await($promise);\n```\n\nThis function will only return after the given `$promise` has settled, i.e.\neither fulfilled or rejected. While the promise is pending, this function\ncan be considered *blocking* from the perspective of the calling code.\nYou can avoid this blocking behavior by wrapping it in an [`async()` function](#async)\ncall. Everything inside this function will still be blocked, but everything\noutside this function can be executed asynchronously without blocking:\n\n```php\nLoop::addTimer(0.5, React\\Async\\async(function () {\n    echo 'a';\n    React\\Async\\await(React\\Promise\\Timer\\sleep(1.0));\n    echo 'c';\n}));\n\nLoop::addTimer(1.0, function () {\n    echo 'b';\n});\n\n// prints \"a\" at t=0.5s\n// prints \"b\" at t=1.0s\n// prints \"c\" at t=1.5s\n```\n\nSee also the [`async()` function](#async) for more details.\n\nOnce the promise is fulfilled, this function will return whatever the promise\nresolved to.\n\nOnce the promise is rejected, this will throw whatever the promise rejected\nwith. If the promise did not reject with an `Exception` or `Throwable`, then\nthis function will throw an `UnexpectedValueException` instead.\n\n```php\ntry {\n    $result = React\\Async\\await($promise);\n    // promise successfully fulfilled with $result\n    echo 'Result: ' . $result;\n} catch (Throwable $e) {\n    // promise rejected with $e\n    echo 'Error: ' . $e-\u003egetMessage();\n}\n```\n\n### coroutine()\n\nThe `coroutine(callable(mixed ...$args):(\\Generator|PromiseInterface\u003cT\u003e|T) $function, mixed ...$args): PromiseInterface\u003cT\u003e` function can be used to\nexecute a Generator-based coroutine to \"await\" promises.\n\n```php\nReact\\Async\\coroutine(function () {\n    $browser = new React\\Http\\Browser();\n\n    try {\n        $response = yield $browser-\u003eget('https://example.com/');\n        assert($response instanceof Psr\\Http\\Message\\ResponseInterface);\n        echo $response-\u003egetBody();\n    } catch (Exception $e) {\n        echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n    }\n});\n```\n\nUsing Generator-based coroutines is an alternative to directly using the\nunderlying promise APIs. For many use cases, this makes using promise-based\nAPIs much simpler, as it resembles a synchronous code flow more closely.\nThe above example performs the equivalent of directly using the promise APIs:\n\n```php\n$browser = new React\\Http\\Browser();\n\n$browser-\u003eget('https://example.com/')-\u003ethen(function (Psr\\Http\\Message\\ResponseInterface $response) {\n    echo $response-\u003egetBody();\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThe `yield` keyword can be used to \"await\" a promise resolution. Internally,\nit will turn the entire given `$function` into a [`Generator`](https://www.php.net/manual/en/class.generator.php).\nThis allows the execution to be interrupted and resumed at the same place\nwhen the promise is fulfilled. The `yield` statement returns whatever the\npromise is fulfilled with. If the promise is rejected, it will throw an\n`Exception` or `Throwable`.\n\nThe `coroutine()` function will always return a Promise which will be\nfulfilled with whatever your `$function` returns. Likewise, it will return\na promise that will be rejected if you throw an `Exception` or `Throwable`\nfrom your `$function`. This allows you to easily create Promise-based\nfunctions:\n\n```php\n$promise = React\\Async\\coroutine(function () {\n    $browser = new React\\Http\\Browser();\n    $urls = [\n        'https://example.com/alice',\n        'https://example.com/bob'\n    ];\n\n    $bytes = 0;\n    foreach ($urls as $url) {\n        $response = yield $browser-\u003eget($url);\n        assert($response instanceof Psr\\Http\\Message\\ResponseInterface);\n        $bytes += $response-\u003egetBody()-\u003egetSize();\n    }\n    return $bytes;\n});\n\n$promise-\u003ethen(function (int $bytes) {\n    echo 'Total size: ' . $bytes . PHP_EOL;\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThe previous example uses a `yield` statement inside a loop to highlight how\nthis vastly simplifies consuming asynchronous operations. At the same time,\nthis naive example does not leverage concurrent execution, as it will\nessentially \"await\" between each operation. In order to take advantage of\nconcurrent execution within the given `$function`, you can \"await\" multiple\npromises by using a single `yield` together with Promise-based primitives\nlike this:\n\n```php\n$promise = React\\Async\\coroutine(function () {\n    $browser = new React\\Http\\Browser();\n    $urls = [\n        'https://example.com/alice',\n        'https://example.com/bob'\n    ];\n\n    $promises = [];\n    foreach ($urls as $url) {\n        $promises[] = $browser-\u003eget($url);\n    }\n\n    try {\n        $responses = yield React\\Promise\\all($promises);\n    } catch (Exception $e) {\n        foreach ($promises as $promise) {\n            $promise-\u003ecancel();\n        }\n        throw $e;\n    }\n\n    $bytes = 0;\n    foreach ($responses as $response) {\n        assert($response instanceof Psr\\Http\\Message\\ResponseInterface);\n        $bytes += $response-\u003egetBody()-\u003egetSize();\n    }\n    return $bytes;\n});\n\n$promise-\u003ethen(function (int $bytes) {\n    echo 'Total size: ' . $bytes . PHP_EOL;\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\n### delay()\n\nThe `delay(float $seconds): void` function can be used to\ndelay program execution for duration given in `$seconds`.\n\n```php\nReact\\Async\\delay($seconds);\n```\n\nThis function will only return after the given number of `$seconds` have\nelapsed. If there are no other events attached to this loop, it will behave\nsimilar to PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php).\n\n```php\necho 'a';\nReact\\Async\\delay(1.0);\necho 'b';\n\n// prints \"a\" at t=0.0s\n// prints \"b\" at t=1.0s\n```\n\nUnlike PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php),\nthis function may not necessarily halt execution of the entire process thread.\nInstead, it allows the event loop to run any other events attached to the\nsame loop until the delay returns:\n\n```php\necho 'a';\nLoop::addTimer(1.0, function (): void {\n    echo 'b';\n});\nReact\\Async\\delay(3.0);\necho 'c';\n\n// prints \"a\" at t=0.0s\n// prints \"b\" at t=1.0s\n// prints \"c\" at t=3.0s\n```\n\nThis behavior is especially useful if you want to delay the program execution\nof a particular routine, such as when building a simple polling or retry\nmechanism:\n\n```php\ntry {\n    something();\n} catch (Throwable) {\n    // in case of error, retry after a short delay\n    React\\Async\\delay(1.0);\n    something();\n}\n```\n\nBecause this function only returns after some time has passed, it can be\nconsidered *blocking* from the perspective of the calling code. You can avoid\nthis blocking behavior by wrapping it in an [`async()` function](#async) call.\nEverything inside this function will still be blocked, but everything outside\nthis function can be executed asynchronously without blocking:\n\n```php\nLoop::addTimer(0.5, React\\Async\\async(function (): void {\n    echo 'a';\n    React\\Async\\delay(1.0);\n    echo 'c';\n}));\n\nLoop::addTimer(1.0, function (): void {\n    echo 'b';\n});\n\n// prints \"a\" at t=0.5s\n// prints \"b\" at t=1.0s\n// prints \"c\" at t=1.5s\n```\n\nSee also the [`async()` function](#async) for more details.\n\nInternally, the `$seconds` argument will be used as a timer for the loop so that\nit keeps running until this timer triggers. This implies that if you pass a\nreally small (or negative) value, it will still start a timer and will thus\ntrigger at the earliest possible time in the future.\n\nThe function is implemented in such a way that it can be cancelled when it is\nrunning inside an [`async()` function](#async). Cancelling the resulting\npromise will clean up any pending timers and throw a `RuntimeException` from\nthe pending delay which in turn would reject the resulting promise.\n\n```php\n$promise = async(function (): void {\n    echo 'a';\n    delay(3.0);\n    echo 'b';\n})();\n\nLoop::addTimer(2.0, function () use ($promise): void {\n    $promise-\u003ecancel();\n});\n\n// prints \"a\" at t=0.0s\n// rejects $promise at t=2.0\n// never prints \"b\"\n```\n\n### parallel()\n\nThe `parallel(iterable\u003ccallable():PromiseInterface\u003cT\u003e\u003e $tasks): PromiseInterface\u003carray\u003cT\u003e\u003e` function can be used\nlike this:\n\n```php\n\u003c?php\n\nuse React\\EventLoop\\Loop;\nuse React\\Promise\\Promise;\n\nReact\\Async\\parallel([\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for a whole second');\n            });\n        });\n    },\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for another whole second');\n            });\n        });\n    },\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for yet another whole second');\n            });\n        });\n    },\n])-\u003ethen(function (array $results) {\n    foreach ($results as $result) {\n        var_dump($result);\n    }\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\n### series()\n\nThe `series(iterable\u003ccallable():PromiseInterface\u003cT\u003e\u003e $tasks): PromiseInterface\u003carray\u003cT\u003e\u003e` function can be used\nlike this:\n\n```php\n\u003c?php\n\nuse React\\EventLoop\\Loop;\nuse React\\Promise\\Promise;\n\nReact\\Async\\series([\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for a whole second');\n            });\n        });\n    },\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for another whole second');\n            });\n        });\n    },\n    function () {\n        return new Promise(function ($resolve) {\n            Loop::addTimer(1, function () use ($resolve) {\n                $resolve('Slept for yet another whole second');\n            });\n        });\n    },\n])-\u003ethen(function (array $results) {\n    foreach ($results as $result) {\n        var_dump($result);\n    }\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\n### waterfall()\n\nThe `waterfall(iterable\u003ccallable(mixed=):PromiseInterface\u003cT\u003e\u003e $tasks): PromiseInterface\u003cT\u003e` function can be used\nlike this:\n\n```php\n\u003c?php\n\nuse React\\EventLoop\\Loop;\nuse React\\Promise\\Promise;\n\n$addOne = function ($prev = 0) {\n    return new Promise(function ($resolve) use ($prev) {\n        Loop::addTimer(1, function () use ($prev, $resolve) {\n            $resolve($prev + 1);\n        });\n    });\n};\n\nReact\\Async\\waterfall([\n    $addOne,\n    $addOne,\n    $addOne\n])-\u003ethen(function ($prev) {\n    echo \"Final result is $prev\\n\";\n}, function (Exception $e) {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\n## Todo\n\n * Implement queue()\n\n## Install\n\nThe recommended way to install this library is [through Composer](https://getcomposer.org/).\n[New to Composer?](https://getcomposer.org/doc/00-intro.md)\n\nThis project follows [SemVer](https://semver.org/).\nThis will install the latest supported version from this branch:\n\n```bash\ncomposer require react/async:^4.3\n```\n\nSee also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.\n\nThis project aims to run on any platform and thus does not require any PHP\nextensions and supports running on PHP 8.1+.\nIt's *highly recommended to use the latest supported PHP version* for this project.\n\nWe're committed to providing long-term support (LTS) options and to provide a\nsmooth upgrade path. If you're using an older PHP version, you may use the\n[`3.x` branch](https://github.com/reactphp/async/tree/3.x) (PHP 7.1+) or\n[`2.x` branch](https://github.com/reactphp/async/tree/2.x) (PHP 5.3+) which both\nprovide a compatible API but do not take advantage of newer language features.\nYou may target multiple versions at the same time to support a wider range of\nPHP versions like this:\n\n```bash\ncomposer require \"react/async:^4 || ^3 || ^2\"\n```\n\n## Tests\n\nTo run the test suite, you first need to clone this repo and then install all\ndependencies [through Composer](https://getcomposer.org/):\n\n```bash\ncomposer install\n```\n\nTo run the test suite, go to the project root and run:\n\n```bash\nvendor/bin/phpunit\n```\n\nOn top of this, we use PHPStan on max level to ensure type safety across the project:\n\n```bash\nvendor/bin/phpstan\n```\n\n## License\n\nMIT, see [LICENSE file](LICENSE).\n\nThis project is heavily influenced by [async.js](https://github.com/caolan/async).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactphp%2Fasync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactphp%2Fasync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactphp%2Fasync/lists"}