Skip to content

Commit

Permalink
[2.x] Fast forward resolved/rejected promises with await
Browse files Browse the repository at this point in the history
This makes `await`ing an already resolved promise significantly faster.

Ported from: reactphp#18
  • Loading branch information
WyriHaximus committed Jan 10, 2022
1 parent ab03f4d commit a6b3c7c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
40 changes: 25 additions & 15 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,41 @@
function await(PromiseInterface $promise)
{
$wait = true;
$resolved = null;
$exception = null;
$resolved = false;
$rejected = false;
$resolvedValue = null;
$rejectedThrowable = null;

$promise->then(
function ($c) use (&$resolved, &$wait) {
$resolved = $c;
function ($c) use (&$resolved, &$resolvedValue, &$wait) {
$resolvedValue = $c;
$resolved = true;
$wait = false;
Loop::stop();
},
function ($error) use (&$exception, &$rejected, &$wait) {
$exception = $error;
function ($error) use (&$rejected, &$rejectedThrowable, &$wait) {
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
if (!$error instanceof \Exception && !$error instanceof \Throwable) {
$error = new \UnexpectedValueException(
'Promise rejected with unexpected value of type ' . (is_object($error) ? get_class($error) : gettype($error))
);
}

$rejectedThrowable = $error;
$rejected = true;
$wait = false;
Loop::stop();
}
);

if ($rejected) {
throw $rejectedThrowable;
}

if ($resolved) {
return $resolvedValue;
}

// Explicitly overwrite argument with null value. This ensure that this
// argument does not show up in the stack trace in PHP 7+ only.
$promise = null;
Expand All @@ -78,17 +95,10 @@ function ($error) use (&$exception, &$rejected, &$wait) {
}

if ($rejected) {
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
if (!$exception instanceof \Exception && !$exception instanceof \Throwable) {
$exception = new \UnexpectedValueException(
'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception))
);
}

throw $exception;
throw $rejectedThrowable;
}

return $resolved;
return $resolvedValue;
}

/**
Expand Down
7 changes: 7 additions & 0 deletions tests/AwaitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi
$this->assertEquals(0, gc_collect_cycles());
}

public function testAlreadyFulfilledPromiseShouldNotSuspendFiber()
{
for ($i = 0; $i < 6; $i++) {
$this->assertSame($i, React\Async\await(React\Promise\resolve($i)));
}
}

public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
{
if (method_exists($this, 'expectException')) {
Expand Down

0 comments on commit a6b3c7c

Please sign in to comment.