Skip to content

Commit c5d53ee

Browse files
committed
Improve await() for asyc() to avoid unneeded futureTick() calls
1 parent 8f01f4b commit c5d53ee

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

src/SimpleFiber.php

+21-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
final class SimpleFiber implements FiberInterface
1111
{
1212
private static ?\Fiber $scheduler = null;
13+
private static ?\Closure $suspend = null;
1314
private ?\Fiber $fiber = null;
1415

1516
public function __construct()
@@ -20,29 +21,45 @@ public function __construct()
2021
public function resume(mixed $value): void
2122
{
2223
if ($this->fiber === null) {
24+
$suspend = static fn() => $value;
2325
if (\Fiber::getCurrent() !== self::$scheduler) {
24-
Loop::futureTick(static fn() => \Fiber::suspend(static fn() => $value));
26+
self::$suspend = $suspend;
2527
} else {
26-
\Fiber::suspend(static fn() => $value);
28+
\Fiber::suspend($suspend);
2729
}
2830
return;
2931
}
3032

3133
$this->fiber->resume($value);
34+
35+
if (self::$suspend) {
36+
$suspend = self::$suspend;
37+
self::$suspend = null;
38+
39+
\Fiber::suspend($suspend);
40+
}
3241
}
3342

3443
public function throw(\Throwable $throwable): void
3544
{
3645
if ($this->fiber === null) {
46+
$suspend = static fn() => throw $throwable;
3747
if (\Fiber::getCurrent() !== self::$scheduler) {
38-
Loop::futureTick(static fn() => \Fiber::suspend(static fn() => throw $throwable));
48+
self::$suspend = $suspend;
3949
} else {
40-
\Fiber::suspend(static fn() => throw $throwable);
50+
\Fiber::suspend($suspend);
4151
}
4252
return;
4353
}
4454

4555
$this->fiber->throw($throwable);
56+
57+
if (self::$suspend) {
58+
$suspend = self::$suspend;
59+
self::$suspend = null;
60+
61+
\Fiber::suspend($suspend);
62+
}
4663
}
4764

4865
public function suspend(): mixed

tests/AwaitTest.php

+54
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use React\EventLoop\Loop;
77
use React\Promise\Deferred;
88
use React\Promise\Promise;
9+
use function React\Async\async;
910

1011
class AwaitTest extends TestCase
1112
{
@@ -68,6 +69,34 @@ public function testAwaitThrowsExceptionImmediatelyWhenPromiseIsRejected(callabl
6869
}
6970
}
7071

72+
/**
73+
* @dataProvider provideAwaiters
74+
*/
75+
public function testAwaitAsyncThrowsExceptionImmediatelyWhenPromiseIsRejected(callable $await)
76+
{
77+
$deferred = new Deferred();
78+
79+
$ticks = 0;
80+
Loop::futureTick(function () use (&$ticks) {
81+
++$ticks;
82+
Loop::futureTick(function () use (&$ticks) {
83+
++$ticks;
84+
});
85+
});
86+
87+
Loop::futureTick(fn() => $deferred->reject(new \RuntimeException()));
88+
89+
$promise = async(function () use ($deferred, $await) {
90+
return $await($deferred->promise());
91+
})();
92+
93+
try {
94+
$await($promise);
95+
} catch (\RuntimeException $e) {
96+
$this->assertEquals(1, $ticks);
97+
}
98+
}
99+
71100
/**
72101
* @dataProvider provideAwaiters
73102
*/
@@ -176,6 +205,31 @@ public function testAwaitReturnsValueImmediatelyWhenPromiseIsFulfilled(callable
176205
$this->assertEquals(1, $ticks);
177206
}
178207

208+
/**
209+
* @dataProvider provideAwaiters
210+
*/
211+
public function testAwaitAsyncReturnsValueImmediatelyWhenPromiseIsFulfilled(callable $await)
212+
{
213+
$deferred = new Deferred();
214+
215+
$ticks = 0;
216+
Loop::futureTick(function () use (&$ticks) {
217+
++$ticks;
218+
Loop::futureTick(function () use (&$ticks) {
219+
++$ticks;
220+
});
221+
});
222+
223+
Loop::futureTick(fn() => $deferred->resolve(42));
224+
225+
$promise = async(function () use ($deferred, $await) {
226+
return $await($deferred->promise());
227+
})();
228+
229+
$this->assertEquals(42, $await($promise));
230+
$this->assertEquals(1, $ticks);
231+
}
232+
179233
/**
180234
* @dataProvider provideAwaiters
181235
*/

0 commit comments

Comments
 (0)