• Stars
    star
    116
  • Rank 302,930 (Top 6 %)
  • Language
    PHP
  • License
    Apache License 2.0
  • Created over 6 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Write code in async/await style in PHP using generators.

Eng | įš | įŽ€

await-generator

Build Status Codecov

A library to use async/await pattern in PHP.

Documentation

Read the await-generator tutorial for an introduction from generators and traditional async callbacks to 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?

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

await-generator has a few common pitfalls:

  • Forgetting to yield from a Generator<void> method will end up doing nothing.
  • If you delete all yields 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?

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

Explicit suspension in type signature

fiber.jpg

For example, it is easy to tell from the type signature that $channel->send($value): Generator<void> 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

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

await-generator provides a feature called "trapping", 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.

More Repositories

1

include-flate

A variant of include_bytes!/include_str! with compile-time deflation and runtime lazy inflation
Rust
113
star
2

pmmp-wilderness

Wiki for common sense for PocketMine Forums. Please refer to the wiki
38
star
3

Capital

An extensible economy API for PocketMine-MP.
PHP
37
star
4

InfoAPI

Placeholder registry and formatting API.
PHP
34
star
5

await-std

Virion for writing PocketMine plugins in await style
PHP
29
star
6

simple-plugins

simple pocketmine plugins for immediate use
PHP
27
star
7

phar.rs

Rust library for PHP phar format
Rust
24
star
8

PocketStorm

PhpStorm plugin for PocketMine plugins
Java
15
star
9

ActionApi

Generalized user interface framework for PocketMine plugins.
PHP
15
star
10

dirmod

Automatic `mod` declaration with visibility/re-export customization, conditional compilation and more.
Rust
14
star
11

FfiPluginLoader

Loads .so files as PocketMine plugins
Rust
14
star
12

Basin

The ultimate PocketMine-MP load balancing solution
PHP
12
star
13

WebConsole

API server and modernized control panel for PocketMine servers.
PHP
12
star
14

libglocal

A virion for localization. Supports parameter declaration and checking, plurals and more.
PHP
11
star
15

GraphMine

An experimental server software project. DO NOT TRY TO USE THIS. This is just a proof of concept.
Kotlin
11
star
16

pharynx

A tool to recompile PHP sources into a phar in PSR-0
PHP
10
star
17

off-side.rs

Use off-side syntax (indent instead of braces, like in Python) to write Rust!
Rust
10
star
18

redox

Redox is a PocketMine server network management system.
Go
10
star
19

dynec

An opinionated ECS-like framework
Rust
9
star
20

nvim-config

Personal nvim config.
Vim Script
8
star
21

brainfuck-cpu

A Logisim schematic for a CPU that implements the Brainfuck language.
Brainfuck
8
star
22

portrait

Fills an `impl` with the associated items required by the trait.
Rust
8
star
23

nbtstreams

Stream-style NBT parser/exporter
PHP
8
star
24

PostBox

Offline messages for players. Supports SQLite and MySQL.
PHP
8
star
25

OctoGuard

OctoGuard is a spam management app for social features on GitHub, such as issue tracker and comments.
Go
7
star
26

webcord

Mirrors Discord chat logs on webpages in a searchable fashion
Rust
7
star
27

await-std-examples

Examples for await-std
PHP
7
star
28

libkinetic

Ultimate MVC solution for PocketMine plugins. See https://github.com/SOF3/PostBox for example usage.
PHP
7
star
29

ChoosePlayer

API plugin to open a dialog for choosing another player.
PHP
7
star
30

pmevent

Await for an event in PocketMine.
PHP
6
star
31

boredphoton

Internal discord bot
Rust
6
star
32

pmdock

Docker for PocketMine developers and plugin developers
Dockerfile
6
star
33

pathetique

An object-oriented path manipulation library in PHP.
PHP
6
star
34

rwlock-promise

JavaScript library providing shared/exclusive locks with Promise API
TypeScript
5
star
35

RpcGenerator

PHP
5
star
36

enclavlow

A Java flow analysis tool for SGX data sensitivity. This is my final year project.
Kotlin
5
star
37

bthint

Discord bot to remind users to use ``` for code
Rust
5
star
38

pschemlib

A virion for schematic file I/O
PHP
5
star
39

xylem

A Rust framework for stateful type conversion.
Rust
5
star
40

pmutil

Useful utilities for PocketMine plugins, with a virion
PHP
4
star
41

await-rt

The low-level runtime of the await-generator library
4
star
42

pemapmodder-killer

A useless plugin that only works on the map in PEMapModder's avatar. Is anybody even gonna use this?
PHP
4
star
43

ModernFactions

A modernized factions plugin
PHP
4
star
44

mosaic-forms-examples

Examples for my mosaic-forms branch
PHP
4
star
45

sys

My personal system setup. This is very specific and just shared for fun.
Shell
4
star
46

php-ext-thisrc

PHP extension to get refcount of $this
C
4
star
47

defy

Replacement for the `yew::html!` macro with more Rust-idiomatic, editor-friendly syntax.
Rust
3
star
48

rwlock.php

Exclusive and Read-write locks for PHP.
PHP
3
star
49

Mentamatics

Android app that generates mental arithmetics problems
Java
3
star
50

octorest

Up-to-date GitHub API bindings generated from @octokit/routes
Rust
3
star
51

willow

Willow is a library for using the WebGL API in WebAssembly projects.
Rust
3
star
52

SOF3

About me
3
star
53

rakrs

RakNet library intended for Minecraft servers
Rust
3
star
54

Depin

A dependency injection library for PHP.
PHP
3
star
55

everydb

A virion for convenient indexed data saving.
PHP
3
star
56

serde-iter

Iterator serialization functions for sequence/map serialization
Rust
3
star
57

GSP

A Generalized Subprocess Plugin (GSP) is a PocketMine plugin written in any language supporting standard IO.
3
star
58

toomuchbuffer

Library for more stream-based data IO
PHP
3
star
59

orbs.bot

Don't ask me why.
TypeScript
2
star
60

aoc2022-haskell

My first Haskell scripts...
Haskell
2
star
61

plugin-system

A Rust project plugin management system using Cargo
Rust
2
star
62

stat4609-project

STAT4609 project, in collaboration with @jevrii and @kellycyy. Git commit authors do not necessarily represent the actual author.
Jupyter Notebook
2
star
63

gh-wrap-long-lines

This Chrome extension wraps the lines on GitHub's blob view pages if they exceed the blob container width. This somehow fixes the decrease in FPS when viewing files with long lines.
JavaScript
2
star
64

libglocal-idea-plugin

IDEA-platform (targetted at PhpStorm) plugin that supports editing [libglocal](https://github.com/SOF3/libglocal) language files.
Kotlin
2
star
65

static-overloads

Use parameter-varying method overloads in PHP
PHP
2
star
66

gusket

Yet another geter/setter derive macro.
Rust
2
star
67

MySQLClientCompact

Android MySQL client using JDBC
Java
2
star
68

IncompatibleAPIPluginLoader

An incompatible API plugin loader for PocketMine-MP - USE AT OWN RISK
PHP
2
star
69

xias

eXplicit Integer AS conversions for rust
Rust
1
star
70

bi_channel.rs

Manage bidirectional mpsc channels more conveniently
Rust
1
star
71

throttle

Time-based rate-limit utility
Rust
1
star
72

OnlineWorld

PHP
1
star
73

ModernEconomy

PHP
1
star
74

qualify-derive

Simple utility for wrapping derive macros that do not qualify paths properly.
Rust
1
star
75

cfg-match

A macro like cfg-if, but for mutually exclusive arm
Rust
1
star
76

bfc-asm

Compile Brainfuck into Assembly (without any optimization)
Rust
1
star
77

plotters-ratatui-backend

A ratatui widget for drawing a plotters chart
Rust
1
star
78

serde-strings

Wrapper of FromStr/Display for serde
Rust
1
star
79

kingdom

KINGDOM = KotlIN-Generated DOM: HTML in Kotlin DSL with CSS optimization
Kotlin
1
star
80

ratio-def

Define newtypes for unit ratios
Rust
1
star
81

phpser

PHP serialization format support
Rust
1
star
82

j2mlog

Java to mindustry logic transpiler
Java
1
star
83

zoom

Become effected for a certain amount of time of your choice with a effect of your choice.
PHP
1
star
84

fstree

Cross-platform harddisk usage diagnostics tool
Rust
1
star
85

webbar

Barcode scanner on the web
1
star
86

zched

Zleep in pocketmine plugin with async-await
1
star
87

AndTransfer

Android app to share files to transfer.sh directly
Kotlin
1
star
88

example-virion-user

Example virion user based on Virion 3.0 spec.
PHP
1
star
89

comp2123-project

TeX
1
star
90

annotated-properties

A PHP library for comment-preserved data serialization of INI-based languages
PHP
1
star
91

InfoChat

A simple chat format plugin powered by InfoAPI.
PHP
1
star
92

slv

Structured Log Viewer, an interactive viewport for output of structured loggers like Logrus.
Rust
1
star
93

bottlenick

Discord bot that rate-limits users from changing their nicks
Rust
1
star
94

aoc2023

advent of code 2023 in rust
Rust
1
star
95

github-stats

Python
1
star
96

volv-prototype

volv game prototype experiment
Rust
1
star