1
0
mirror of https://github.com/danog/amp.git synced 2024-12-02 17:37:50 +01:00

Refactor Coroutine to use await()

Dropped polyfill, ext-fiber will be required to run any v3 code.
This commit is contained in:
Aaron Piotrowski 2020-09-26 22:22:07 -05:00
parent 9f68bd4046
commit b65823e0bb
No known key found for this signature in database
GPG Key ID: ADD1EF783EDE9EEB
3 changed files with 14 additions and 136 deletions

View File

@ -33,10 +33,10 @@
} }
], ],
"require": { "require": {
"php": ">=8" "php": ">=8",
"ext-fiber": "*"
}, },
"require-dev": { "require-dev": {
"ext-fiber": "*",
"ext-json": "*", "ext-json": "*",
"amphp/phpunit-util": "^1", "amphp/phpunit-util": "^1",
"amphp/php-cs-fixer-config": "dev-master", "amphp/php-cs-fixer-config": "dev-master",
@ -51,8 +51,7 @@
}, },
"files": [ "files": [
"lib/functions.php", "lib/functions.php",
"lib/Internal/functions.php", "lib/Internal/functions.php"
"polyfill/ext-fiber.php"
] ]
}, },
"autoload-dev": { "autoload-dev": {

View File

@ -2,9 +2,9 @@
namespace Amp; namespace Amp;
use React\Promise\PromiseInterface as ReactPromise;
/** /**
* @deprecated Use {@see await()} and ext-fiber to await promises.
*
* Creates a promise from a generator function yielding promises. * Creates a promise from a generator function yielding promises.
* *
* When a promise is yielded, execution of the generator is interrupted until the promise is resolved. A success * When a promise is yielded, execution of the generator is interrupted until the promise is resolved. A success
@ -18,44 +18,6 @@ final class Coroutine implements Promise
{ {
use Internal\Placeholder; use Internal\Placeholder;
/**
* Attempts to transform the non-promise yielded from the generator into a promise, otherwise returns an instance
* `Amp\Failure` failed with an instance of `Amp\InvalidYieldError`.
*
* @param mixed $yielded Non-promise yielded from generator.
* @param \Generator $generator No type for performance, we already know the type.
*
* @return Promise
*/
private static function transform(mixed $yielded, \Generator $generator): Promise
{
$exception = null; // initialize here, see https://github.com/vimeo/psalm/issues/2951
try {
if (\is_array($yielded)) {
return Promise\all($yielded);
}
if ($yielded instanceof ReactPromise) {
return Promise\adapt($yielded);
}
// No match, continue to returning Failure below.
} catch (\Throwable $exception) {
// Conversion to promise failed, fall-through to returning Failure below.
}
return new Failure(new InvalidYieldError(
$generator,
\sprintf(
"Unexpected yield; Expected an instance of %s or %s or an array of such instances",
Promise::class,
ReactPromise::class
),
$exception
));
}
/** /**
* @param \Generator $generator * @param \Generator $generator
* @psalm-param \Generator<mixed,Promise|ReactPromise|array<array-key, * @psalm-param \Generator<mixed,Promise|ReactPromise|array<array-key,
@ -63,98 +25,21 @@ final class Coroutine implements Promise
*/ */
public function __construct(\Generator $generator) public function __construct(\Generator $generator)
{ {
try { $this->resolve(async(function () use ($generator): mixed {
$yielded = $generator->current(); $yielded = $generator->current();
if (!$yielded instanceof Promise) { while ($generator->valid()) {
if (!$generator->valid()) {
$this->resolve($generator->getReturn());
return;
}
$yielded = self::transform($yielded, $generator);
}
} catch (\Throwable $exception) {
$this->fail($exception);
return;
}
/**
* @param \Throwable|null $e Exception to be thrown into the generator.
* @param mixed $v Value to be sent into the generator.
*
* @return void
*
* @psalm-suppress MissingClosureParamType
* @psalm-suppress MissingClosureReturnType
*/
$onResolve = function (?\Throwable $e, mixed $v) use ($generator, &$onResolve): void {
/** @var bool $immediate Used to control iterative coroutine continuation. */
static $immediate = true;
/** @var \Throwable|null $exception Promise failure reason when executing next coroutine step, null at all other times. */
static $exception;
/** @var mixed $value Promise success value when executing next coroutine step, null at all other times. */
static $value;
$exception = $e;
/** @psalm-suppress MixedAssignment */
$value = $v;
if (!$immediate) {
$immediate = true;
return;
}
try {
try { try {
do { $value = await($yielded);
if ($exception) {
// Throw exception at current execution point.
$yielded = $generator->throw($exception);
} else {
// Send the new value and execute to next yield statement.
$yielded = $generator->send($value);
}
if (!$yielded instanceof Promise) {
if (!$generator->valid()) {
$this->resolve($generator->getReturn());
$onResolve = null;
return;
}
$yielded = self::transform($yielded, $generator);
}
$immediate = false;
$yielded->onResolve($onResolve);
} while ($immediate);
$immediate = true;
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
$this->fail($exception); $yielded = $generator->throw($exception);
$onResolve = null; continue;
} finally {
$exception = null;
$value = null;
} }
} catch (\Throwable $e) {
Loop::defer(static function () use ($e) { $yielded = $generator->send($value);
throw $e;
});
} }
};
try { return $generator->getReturn();
$yielded->onResolve($onResolve); }));
unset($generator, $yielded, $onResolve);
} catch (\Throwable $e) {
Loop::defer(static function () use ($e) {
throw $e;
});
}
} }
} }

View File

@ -1,6 +0,0 @@
<?php
if (!\extension_loaded('fiber')) {
require __DIR__ . '/../stubs/Awaitable.php';
require __DIR__ . '/../stubs/FiberScheduler.php';
}