Skip to content

Commit 2854f1f

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

File tree

2 files changed

+44
-14
lines changed

2 files changed

+44
-14
lines changed

src/functions.php

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,20 @@
5151
function await(PromiseInterface $promise)
5252
{
5353
$wait = true;
54-
$resolved = null;
55-
$exception = null;
54+
$resolved = false;
5655
$rejected = false;
56+
$resolvedValue = null;
57+
$rejectedThrowable = null;
5758

5859
$promise->then(
59-
function ($c) use (&$resolved, &$wait) {
60-
$resolved = $c;
60+
function ($c) use (&$resolved, &$resolvedValue, &$wait) {
61+
$resolvedValue = $c;
62+
$resolved = true;
6163
$wait = false;
6264
Loop::stop();
6365
},
64-
function ($error) use (&$exception, &$rejected, &$wait) {
65-
$exception = $error;
66+
function ($error) use (&$rejected, &$rejectedThrowable, &$wait) {
67+
$rejectedThrowable = $error;
6668
$rejected = true;
6769
$wait = false;
6870
Loop::stop();
@@ -73,22 +75,43 @@ function ($error) use (&$exception, &$rejected, &$wait) {
7375
// argument does not show up in the stack trace in PHP 7+ only.
7476
$promise = null;
7577

78+
if ($rejected) {
79+
awaitThrow($rejectedThrowable);
80+
}
81+
82+
if ($resolved) {
83+
return $resolvedValue;
84+
}
85+
7686
while ($wait) {
7787
Loop::run();
7888
}
7989

8090
if ($rejected) {
81-
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
82-
if (!$exception instanceof \Exception && !$exception instanceof \Throwable) {
83-
$exception = new \UnexpectedValueException(
84-
'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception))
85-
);
86-
}
91+
awaitThrow($rejectedThrowable);
92+
}
8793

88-
throw $exception;
94+
return $resolvedValue;
95+
}
96+
97+
/**
98+
* This function is for internal use only. But it wraps the data a promise is rejected with in a \UnexpectedValueException
99+
* when it isn't an \Exception or a \Throwable.
100+
*
101+
* @internal
102+
* @param \Exception|\Throwable $rejectedThrowable
103+
* @throws \Exception|\Throwable
104+
*/
105+
function awaitThrow($rejectedThrowable)
106+
{
107+
// promise is rejected with an unexpected value (Promise API v1 or v2 only)
108+
if (!$rejectedThrowable instanceof \Exception && !$rejectedThrowable instanceof \Throwable) {
109+
$rejectedThrowable = new \UnexpectedValueException(
110+
'Promise rejected with unexpected value of type ' . (is_object($rejectedThrowable) ? get_class($rejectedThrowable) : gettype($rejectedThrowable))
111+
);
89112
}
90113

91-
return $resolved;
114+
throw $rejectedThrowable;
92115
}
93116

94117
/**

tests/AwaitTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi
145145
$this->assertEquals(0, gc_collect_cycles());
146146
}
147147

148+
public function testAlreadyFulfilledPromiseShouldNotSuspendFiber()
149+
{
150+
for ($i = 0; $i < 6; $i++) {
151+
$this->assertSame($i, React\Async\await(React\Promise\resolve($i)));
152+
}
153+
}
154+
148155
public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
149156
{
150157
if (method_exists($this, 'expectException')) {

0 commit comments

Comments
 (0)