{"id":15636501,"url":"https://github.com/sof3/await-generator","last_synced_at":"2025-05-16T05:06:14.946Z","repository":{"id":32591510,"uuid":"137670772","full_name":"SOF3/await-generator","owner":"SOF3","description":"Write code in async/await style in PHP using generators.","archived":false,"fork":false,"pushed_at":"2025-02-21T03:32:34.000Z","size":1927,"stargazers_count":124,"open_issues_count":7,"forks_count":15,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-11T06:06:13.077Z","etag":null,"topics":["async-await","async-generator","await","php","php-async","virion"],"latest_commit_sha":null,"homepage":"https://sof3.github.io/await-generator/master/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SOF3.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}},"created_at":"2018-06-17T16:51:05.000Z","updated_at":"2025-04-18T12:45:22.000Z","dependencies_parsed_at":"2023-12-27T04:46:44.007Z","dependency_job_id":"80d7de4d-4c21-4c95-ba8a-20504f4b906c","html_url":"https://github.com/SOF3/await-generator","commit_stats":{"total_commits":258,"total_committers":11,"mean_commits":"23.454545454545453","dds":0.3798449612403101,"last_synced_commit":"2241e708ba6b0cac0216fc10aeb3d8cf217a797a"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SOF3%2Fawait-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SOF3%2Fawait-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SOF3%2Fawait-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SOF3%2Fawait-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SOF3","download_url":"https://codeload.github.com/SOF3/await-generator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254471061,"owners_count":22076585,"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-await","async-generator","await","php","php-async","virion"],"created_at":"2024-10-03T11:04:29.035Z","updated_at":"2025-05-16T05:06:14.491Z","avatar_url":"https://github.com/SOF3.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Eng | [繁](zho) | [简](chs)\n# await-generator\n[![Build Status][ci-badge]][ci-page]\n[![Codecov][codecov-badge]][codecov-page]\n\nA library to use async/await pattern in PHP.\n\n## Documentation\nRead the [await-generator tutorial][book] for an introduction\nfrom generators and traditional async callbacks to await-generator.\n\n## Why await-generator?\nTraditional async programming requires callbacks,\nwhich leads to spaghetti code known as \"callback hell\":\n\u003cdetails\u003e\n    \u003csummary\u003eClick to reveal example callback hell\u003c/summary\u003e\n    \n```php\nload_data(function($data) {\n    $init = count($data) === 0 ? init_data(...) : fn($then) =\u003e $then($data);\n    $init(function($data) {\n        $output = [];\n        foreach($data as $k =\u003e $datum) {\n            processData($datum, function($result) use(\u0026$output, $data) {\n                $output[$k] = $result;\n                if(count($output) === count($data)) {\n                    createQueries($output, function($queries) {\n                        $run = function($i) use($queries, \u0026$run) {\n                            runQuery($queries[$i], function() use($i, $queries, $run) {\n                                if($i === count($queries)) {\n                                    $done = false;\n                                    commitBatch(function() use(\u0026$done) {\n                                        if(!$done) {\n                                            $done = true;\n                                            echo \"Done!\\n\";\n                                        }\n                                    });\n                                    onUserClose(function() use(\u0026$done) {\n                                        if(!$done) {\n                                            $done = true;\n                                            echo \"User closed!\\n\";\n                                        }\n                                    });\n                                    onTimeout(function() use(\u0026$done) {\n                                        if(!$done) {\n                                            $done = true;\n                                            echo \"Timeout!\\n\";\n                                        }\n                                    });\n                                } else {\n                                    $run($i + 1);\n                                }\n                            });\n                        };\n                    });\n                }\n            });\n        }\n    });\n});\n```\n    \n\u003c/details\u003e\nWith await-generator, this is simplified into:\n\n```php\n$data = yield from load_data();\nif(count($data) === 0) $data = yield from init_data();\n$output = yield from Await::all(array_map(fn($datum) =\u003e processData($datum), $data));\n$queries = yield from createQueries($output);\nforeach($queries as $query) yield from runQuery($query);\n[$which, ] = yield from Await::race([\n    0 =\u003e commitBatch(),\n    1 =\u003e onUserClose(),\n    2 =\u003e onTimeout(),\n])\necho match($which) {\n    0 =\u003e \"Done!\\n\",\n    1 =\u003e \"User closed!\\n\",\n    2 =\u003e \"Timeout!\\n\",\n};\n```\n\n## Can I maintain backward compatibility?\nYes, await-generator does not impose any restrictions on your existing API.\nYou can wrap all await-generator calls as internal implementation detail,\nalthough you are strongly encouraged to expose the generator functions directly.\n\nawait-generator starts an await context with the `Await::f2c` method,\nwith which you can adapt into the usual callback syntax:\n\n```php\nfunction oldApi($args, Closure $onSuccess) {\n    Await::f2c(fn() =\u003e $onSuccess(yield from newApi($args)));\n}\n```\n\nOr if you want to handle errors too:\n\n```php\nfunction newApi($args, Closure $onSuccess, Closure $onError) {\n    Await::f2c(function() use($onSuccess, $onError) {\n        try {\n            $onSuccess(yield from newApi($args));\n        } catch(Exception $ex) {\n            $onError($ex);\n        }\n    });\n}\n```\n\nYou can continue to call functions implemented as callback style\nusing the `Await::promise` method (similar to `new Promise` in JS):\n\n```php\nyield from Await::promise(fn($resolve, $reject) =\u003e oldFunction($args, $resolve, $reject));\n```\n\n## Why *not* await-generator\nawait-generator has a few common pitfalls:\n\n- Forgetting to `yield from` a `Generator\u003cvoid\u003e` method will end up doing nothing.\n- If you delete all `yield`s from a function,\n  it automatically becomes a non-generator function thanks to PHP magic.\n  This issue can be mitigated by always adding `: Generator` to the function signature.\n- `finally` blocks may never get executed if an async function never resolves\n  (e.g. `Await::promise(fn($resolve) =\u003e null)`).\n\nWhile these pitfalls cause some trouble,\nawait-generator style is still much less bug-prone than a callback hell.\n\n## But what about fibers?\nThis might be a subjective comment,\nbut I do not prefer fibers for a few reasons:\n\n### Explicit suspension in type signature\n![fiber.jpg](./fiber.jpeg)\n\nFor example, it is easy to tell from the type signature that\n`$channel-\u003esend($value): Generator\u003cvoid\u003e` suspends until the value is sent\nand `$channel-\u003esendBuffered($value): void`\nis a non-suspending method that returns immediately.\nType signatures are often self-explanatory.\n\nOf course, users could call `sleep()` anyway,\nbut it is quite obvious to everyone that `sleep()` blocks the whole runtime\n(if they didn't already know, they will find out when the whole world stops).\n\n### Concurrent states\nWhen a function suspends, many other things can happen.\nIndeed, calling a function allows the implementation to call any other functions\nwhich could modify your states anyway,\nbut a sane, genuine implementation of e.g. an HTTP request\nwouldn't call functions that modify the private states of your library.\nBut this assumption does not hold with fibers\nbecause the fiber is preempted and other fibers can still modify the private states.\nThis means you have to check for possible changes in private properties\nevery time you call any function that *might* be suspending.\n\nOn the other hand, using explicit await,\nit is obvious where exactly the suspension points are,\nand you only need to check for state mutations at the known suspension points.\n\n### Trapping suspension points\nawait-generator provides a feature called [\"trapping\"][trap-pr],\nwhich allows users to add pre-suspend and pre-resume hooks to a generator.\nThis is simply achieved by adding an adapter to the generator,\nand does not even require explicit support from the await-generator runtime.\nThis is currently not possible with fibers.\n\n[book]: https://sof3.github.io/await-generator/master/\n[ci-badge]: https://github.com/SOF3/await-generator/workflows/CI/badge.svg\n[ci-page]: https://github.com/SOF3/await-generator/actions?query=workflow%3ACI\n[codecov-badge]: https://img.shields.io/codecov/c/github/codecov/example-python.svg\n[codecov-page]: https://codecov.io/gh/SOF3/await-generator\n[trap-pr]: https://github.com/SOF3/await-generator/pull/106\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsof3%2Fawait-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsof3%2Fawait-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsof3%2Fawait-generator/lists"}