Hi Edmond,
First of all, sorry for my bad English, and thanks a lot for the huge
amount of work you’ve put into this proposal.
You researched, wrote the RFC, implemented it, and answered tons of
questions. Really impressive.
I have one suggestion and two small questions.
*Suggestion*
Maybe keep the base |Awaitable| internal and expose two userland
interfaces that match the two cases described in the RFC:
|// Single state change, idempotent read, same result on each await
interface Future extends Awaitable {} // Multiple state changes, each
await may observe a new state interface Streamable extends Awaitable {} |
This makes the single-shot vs multi-shot difference explicit and easier
for tools and libraries to reason about.
Later on, it could even be extended with something like:
|interface Retryable extends Awaitable {}|
*Questions (self-cancellation)*
1.
What happens here?
|use function Async\spawn; use function Async\suspend; $coroutine =
spawn(function() use (&$coroutine) { $coroutine->cancel(new
\Async\CancellationError("Self-cancelled")); echo "Before suspend\n";
suspend(); echo "After suspend\n"; // should this run? return
"completed"; }); await($coroutine); |
Can a cancelled coroutine suspend?
And if a function that yields is called after the cancel, should that
suspension still happen?
2.
And what about this one?
|use function Async\spawn; $coroutine2 = spawn(function() use
(&$coroutine2) { $coroutine2->cancel(new
\Async\CancellationError("Self-cancelled")); echo "Before exception\n";
throw new \RuntimeException("boom after cancel"); }); await($coroutine2); |
Which error does |await()| throw in this case — |CancellationError| or
|RuntimeException|?
It’d be great to clarify that in the docs, since it affects where people
put cleanup, logging, etc.
Again, thanks for the work, especially on such an important feature for
PHP’s future.
Hope to see it in php-src soon.
Best,
*Luís Vinícius*