{"id":13579285,"url":"https://github.com/recoilphp/recoil","last_synced_at":"2025-04-05T20:33:38.740Z","repository":{"id":9342951,"uuid":"11192116","full_name":"recoilphp/recoil","owner":"recoilphp","description":"Asynchronous coroutines for PHP 7.","archived":false,"fork":false,"pushed_at":"2024-11-06T20:41:35.000Z","size":1255,"stargazers_count":791,"open_issues_count":5,"forks_count":38,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-03-18T20:41:22.263Z","etag":null,"topics":["async","asynchronous","continuation","coroutines","event","generator","green","php-generator","reactor","reactphp"],"latest_commit_sha":null,"homepage":"","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/recoilphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-07-05T05:11:27.000Z","updated_at":"2025-02-25T13:53:11.000Z","dependencies_parsed_at":"2022-08-03T00:15:22.867Z","dependency_job_id":null,"html_url":"https://github.com/recoilphp/recoil","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recoilphp%2Frecoil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recoilphp%2Frecoil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recoilphp%2Frecoil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recoilphp%2Frecoil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/recoilphp","download_url":"https://codeload.github.com/recoilphp/recoil/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399874,"owners_count":20932876,"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":["async","asynchronous","continuation","coroutines","event","generator","green","php-generator","reactor","reactphp"],"created_at":"2024-08-01T15:01:38.034Z","updated_at":"2025-04-05T20:33:33.732Z","avatar_url":"https://github.com/recoilphp.png","language":"PHP","readme":"# Recoil\n\n[![Build Status](http://img.shields.io/travis/recoilphp/recoil/master.svg?style=flat-square)](https://travis-ci.org/recoilphp/recoil)\n[![Code Coverage](https://img.shields.io/codecov/c/github/recoilphp/recoil/master.svg?style=flat-square)](https://codecov.io/github/recoilphp/recoil)\n[![Code Quality](https://img.shields.io/scrutinizer/g/recoilphp/recoil/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/recoilphp/recoil/)\n[![Latest Version](http://img.shields.io/packagist/v/recoil/recoil.svg?style=flat-square\u0026label=semver)](https://semver.org)\n\nAn asynchronous coroutine kernel for PHP 7.\n\n    composer require recoil/recoil\n\nThe Recoil project comprises the following packages:\n\n- [recoil/api](https://github.com/recoilphp/api) - The public Recoil API for application and library developers.\n- [recoil/dev](https://github.com/recoilphp/dev) - Development and debugging tools.\n- [recoil/recoil](https://github.com/recoilphp/recoil) (this package) - A reference implementation of the kernel described in the API.\n- [recoil/react](https://github.com/recoilphp/react) - A kernel implementation based on the [ReactPHP](https://github.com/reactphp/reactphp) event loop.\n- [recoil/kernel](https://github.com/recoilphp/kernel) - Common components used to implement the kernels.\n\n## Overview\n\nRecoil aims to ease development of asynchronous applications by presenting asynchronous control flow in a familiar\n\"imperative\" syntax.\n\n**What does that mean?** Let's jump right in with an example that resolves multiple domain names **concurrently**.\n\n```php\nuse Recoil\\React\\ReactKernel;\nuse Recoil\\Recoil;\n\nfunction resolveDomainName(string $name, React\\Dns\\Resolver\\Resolver $resolver)\n{\n    try {\n        $ip = yield $resolver-\u003eresolve($name);\n        echo 'Resolved \"' . $name . '\" to ' . $ip . PHP_EOL;\n    } catch (Exception $e) {\n        echo 'Failed to resolve \"' . $name . '\" - ' . $e-\u003egetMessage() . PHP_EOL;\n    }\n}\n\nReactKernel::start(function () {\n    // Create a React DNS resolver ...\n    $resolver = (new React\\Dns\\Resolver\\Factory)-\u003ecreate(\n        '8.8.8.8',\n        yield Recoil::eventLoop()\n    );\n\n    // Concurrently resolve three domain names ...\n    yield [\n        resolveDomainName('recoil.io', $resolver),\n        resolveDomainName('php.net', $resolver),\n        resolveDomainName('probably-wont-resolve', $resolver),\n    ];\n});\n```\n\nThis code resolves three domain names to their IP address and prints the results to the terminal. You can try the\nexample yourself by running the following command in the root of the repository:\n\n```\n./examples/dns\n```\n\nRun it a few times. You'll notice that the output is not always in the same order. This is because the requests are made\nconcurrently and the results are shown as soon as they are received from the DNS server.\n\nNote that there is **no callback-passing**, and that regular PHP **exceptions are used for reporting errors**. This is\nwhat we mean by \"familiar imperative syntax\".\n\n**Clear as mud?** Read on :)\n\n## Concepts\n\n### Coroutines\n\n_Coroutines_ are essentially functions that can be suspended and resumed while maintaining their state. This is useful\nin asynchronous applications, as the coroutine can suspend while waiting for some task to complete or information\nto arrive, and the CPU is free to perform other tasks.\n\nPHP generators provide the language-level support for functions that can suspend and resume, and Recoil provides the\nglue that lets us use these features to perform asynchronous operations.\n\nA Recoil application is started by executing an \"entry-point\" generator, a little like the `main()` function in the C\nprogramming language. The Recoil kernel inspects the values yielded by the generator and identifies an operation to\nperform. For example, yielding a `float` with value `30` causes the coroutine to suspend execution for 30 seconds.\n\nThe DNS example above shows a rather more advanced usage, including concurrent execution and integration with\nasynchronous code that is not part of Recoil. The resulting code, however, is quite normal looking, except for the\n`yield` statements!\n\nWithin Recoil, the term _coroutine_ specifically refers to a PHP generator that is being executed by the Recoil kernel.\nIt's no mistake that generators can be used in this way. [Nikita Popov](https://github.com/nikic) (who is responsible\nfor the original generator implementation in PHP) published an [excellent article](http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html)\nexplaining generator-based coroutines. The article even includes an example implementation of a coroutine scheduler,\nthough it takes a somewhat different approach.\n\n### Strands\n\nA _Strand_ is Recoil's equivalent to your operating system's threads. Each strand has its own call-stack and may be\nsuspended, resumed, joined and terminated without affecting other strands. The elements on the call-stack are not\nregular functions, but are instead coroutines.\n\nUnlike threads, execution of a strand can only suspend or resume when a coroutine specifically requests to do so, hence\nthe term _cooperative multitasking_.\n\nStrands are very light-weight and are sometimes known as [green threads](http://en.wikipedia.org/wiki/Green_threads), or\n(perhaps less correctly) as [fibers](https://en.wikipedia.org/wiki/Fiber_(computer_science)).\n\nRecoil's concept of the strand is defined by the [Strand](https://github.com/recoilphp/api/blob/master/src/Strand.php) interface.\n\n### Dispatchable Values\n\nAn _Dispatchable Value_ is any value that Recoil recognises when yielded by a coroutine. For example, yielding another generator\npushes that generator onto the current strand's call-stack and invokes it, thus making it a coroutine.\n\nThe [Recoil facade](https://github.com/recoilphp/api/blob/master/src/Recoil.php) class describes the complete list of\nsupported values.\n\n### The Kernel and Kernel API\n\nThe _kernel_ is responsible for creating and scheduling strands, much like the operating system kernel does for threads.\n\nThe kernel and strands are manipulated using the _kernel API_, which is a set of standard operations defined in the\n[Recoil API](https://github.com/recoilphp/api) and accessible using the [Recoil facade](https://github.com/recoilphp/api/blob/master/src/Recoil.php).\n\nThere are multiple kernel implementations available. This repository contains a stand-alone implementation based on\n`stream_select()`. The [`recoil/react` package](https://github.com/recoilphp/react) provides a kernel based on the [ReactPHP](https://github.com/reactphp/react) event-loop.\n\n## Examples\n\nThe following examples illustrate the basic usage of coroutines and the kernel API. Additional examples are available in\nthe [examples folder](examples/).\n\nReferences to `Recoil` and `ReactKernel` refer to the [Recoil facade](https://github.com/recoilphp/api/blob/master/src/Recoil.php),\nand the [React kernel implementation](https://github.com/recoilphp/react),\nrespectively.\n\n### Basic execution\n\nThe following example shows the simplest way to execute a generator as a coroutine.\n\n```php\nReactKernel::start(\n    function () {\n        echo 'Hello, world!' . PHP_EOL;\n        yield;\n    }\n);\n```\n\n`ReactKernel::start()` is a convenience method that instantiates the React-based kernel and executes the given coroutine\nin a new strand. Yielding `null` (via `yield` with no explicit value) allows PHP to parse the function as a generator,\nand allows the kernel to process other strands, though there are none in this example.\n\n### Calling one coroutine from another\n\nA coroutine can be invoked by simply yielding it, as described in the section on coroutines above. You can also use the\n`yield from` syntax, which may perform better but only works with generators, whereas `yield` works with any dispatchable\nvalue.\n\n```php\nfunction hello()\n{\n    echo 'Hello, ';\n    yield;\n}\n\nfunction world()\n{\n    echo 'world!' . PHP_EOL;\n    yield;\n}\n\nReactKernel::start(function () {\n    yield hello();\n    yield world();\n});\n```\n\n### Returning a value from a coroutine\n\nTo return a value from a coroutine, simply use the `return` keyword as you would in a normal function.\n\n```php\nfunction multiply($a, $b)\n{\n    yield; // force PHP to parse this function as a generator\n    return $a * $b;\n    echo 'This code is never reached.';\n}\n\nReactKernel::start(function () {\n    $result = yield multiply(2, 3);\n    echo '2 * 3 is ' . $result . PHP_EOL;\n});\n```\n\n### Throwing and catching exceptions\n\nOne of the major syntactic advantages of coroutines over callbacks is that errors can be reported using familiar\nexception handling techniques. The `throw` keyword can be used in in a coroutine just as it can in a regular function.\n\n```php\nfunction multiply($a, $b)\n{\n    if (!is_numeric($a) || !is_numeric($b)) {\n        throw new InvalidArgumentException();\n    }\n\n    yield; // force PHP to parse this function as a generator\n    return $a * $b;\n}\n\nReactKernel::start(function() {\n    try {\n        yield multiply(1, 'foo');\n    } catch (InvalidArgumentException $e) {\n        echo 'Invalid argument!';\n    }\n});\n```\n","funding_links":[],"categories":["PHP","异步网络通信框架( Asynchronous Event Driven Framework )","类库"],"sub_categories":["异步/协程"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frecoilphp%2Frecoil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frecoilphp%2Frecoil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frecoilphp%2Frecoil/lists"}