Skip to content

Commit 584e5b0

Browse files
committed
Fast forward resolved/rejected promises with await
This makes `await`ing an already resolved promise significantly faster. Ported from: reactphp#18
1 parent c989ee1 commit 584e5b0

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

src/functions.php

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,20 @@
5353
function await(PromiseInterface $promise)
5454
{
5555
$wait = true;
56-
$resolved = null;
57-
$exception = null;
56+
$resolved = false;
5857
$rejected = false;
58+
$resolvedValue = null;
59+
$rejectedThrowable = null;
5960

6061
$promise->then(
61-
function ($c) use (&$resolved, &$wait) {
62-
$resolved = $c;
62+
function ($c) use (&$resolved, &$resolvedValue, &$wait) {
63+
$resolvedValue = $c;
64+
$resolved = true;
6365
$wait = false;
6466
Loop::stop();
6567
},
66-
function ($error) use (&$exception, &$rejected, &$wait) {
67-
$exception = $error;
68+
function ($error) use (&$rejected, &$rejectedThrowable, &$wait) {
69+
$rejectedThrowable = $error;
6870
$rejected = true;
6971
$wait = false;
7072
Loop::stop();
@@ -75,24 +77,40 @@ function ($error) use (&$exception, &$rejected, &$wait) {
7577
// argument does not show up in the stack trace in PHP 7+ only.
7678
$promise = null;
7779

80+
if ($rejected) {
81+
awaitThrow($rejectedThrowable);
82+
}
83+
84+
if ($resolved) {
85+
return $resolvedValue;
86+
}
87+
7888
while ($wait) {
7989
Loop::run();
8090
}
8191

8292
if ($rejected) {
83-
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
84-
if (!$exception instanceof \Throwable) {
85-
$exception = new \UnexpectedValueException(
86-
'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception))
87-
);
88-
}
89-
90-
throw $exception;
93+
awaitThrow($rejectedThrowable);
9194
}
9295

93-
return $resolved;
96+
return $resolvedValue;
9497
}
9598

99+
/**
100+
* @internal
101+
* @param \Exception|\Throwable $rejectedThrowable
102+
*/
103+
function awaitThrow($rejectedThrowable)
104+
{
105+
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
106+
if (!$rejectedThrowable instanceof \Exception && !$rejectedThrowable instanceof \Throwable) {
107+
$rejectedThrowable = new \UnexpectedValueException(
108+
'Promise rejected with unexpected value of type ' . (is_object($rejectedThrowable) ? get_class($rejectedThrowable) : gettype($rejectedThrowable))
109+
);
110+
}
111+
112+
throw $rejectedThrowable;
113+
}
96114

97115
/**
98116
* Execute a Generator-based coroutine to "await" promises.

tests/AwaitTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise()
122122
$this->assertEquals(0, gc_collect_cycles());
123123
}
124124

125+
public function testAlreadyFulfilledPromiseShouldNotSuspendFiber()
126+
{
127+
for ($i = 0; $i < 6; $i++) {
128+
$this->assertSame($i, React\Async\await(React\Promise\resolve($i)));
129+
}
130+
}
131+
125132
public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithNullValue()
126133
{
127134
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {

0 commit comments

Comments
 (0)