Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 19998f8

Browse files
authored
Merge pull request #33 from SimonFrings/resolved
[3.x] Only stop loop if a pending promise resolves/rejects
2 parents c989ee1 + 42d343b commit 19998f8

File tree

2 files changed

+155
-4
lines changed

2 files changed

+155
-4
lines changed

src/functions.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,25 @@ function await(PromiseInterface $promise)
5656
$resolved = null;
5757
$exception = null;
5858
$rejected = false;
59+
$loopStarted = false;
5960

6061
$promise->then(
61-
function ($c) use (&$resolved, &$wait) {
62+
function ($c) use (&$resolved, &$wait, &$loopStarted) {
6263
$resolved = $c;
6364
$wait = false;
64-
Loop::stop();
65+
66+
if ($loopStarted) {
67+
Loop::stop();
68+
}
6569
},
66-
function ($error) use (&$exception, &$rejected, &$wait) {
70+
function ($error) use (&$exception, &$rejected, &$wait, &$loopStarted) {
6771
$exception = $error;
6872
$rejected = true;
6973
$wait = false;
70-
Loop::stop();
74+
75+
if ($loopStarted) {
76+
Loop::stop();
77+
}
7178
}
7279
);
7380

@@ -76,6 +83,7 @@ function ($error) use (&$exception, &$rejected, &$wait) {
7683
$promise = null;
7784

7885
while ($wait) {
86+
$loopStarted = true;
7987
Loop::run();
8088
}
8189

tests/AwaitTest.php

+143
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,149 @@ public function testAwaitReturnsValueWhenPromiseIsFulfilledEvenWhenOtherTimerSto
8484
$this->assertEquals(2, React\Async\await($promise));
8585
}
8686

87+
public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutRunningLoop()
88+
{
89+
$now = true;
90+
91+
Loop::futureTick(function () use (&$now) {
92+
$now = false;
93+
});
94+
95+
$promise = new Promise(function ($resolve) {
96+
$resolve(42);
97+
});
98+
99+
React\Async\await($promise);
100+
$this->assertTrue($now);
101+
}
102+
103+
public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutStoppingLoop()
104+
{
105+
$ticks = 0;
106+
107+
$promise = new Promise(function ($resolve) {
108+
$resolve(42);
109+
});
110+
111+
// Loop will execute this tick first
112+
Loop::futureTick(function () use (&$ticks) {
113+
++$ticks;
114+
// Loop will execute this tick third
115+
Loop::futureTick(function () use (&$ticks) {
116+
++$ticks;
117+
});
118+
});
119+
120+
// Loop will execute this tick second
121+
Loop::futureTick(function () use (&$promise){
122+
// await won't stop the loop if promise already resolved -> third tick will trigger
123+
React\Async\await($promise);
124+
});
125+
126+
Loop::run();
127+
128+
$this->assertEquals(2, $ticks);
129+
}
130+
131+
public function testAwaitWithPendingPromiseThatWillResolveWillStopLoopBeforeLastTimerFinishes()
132+
{
133+
$promise = new Promise(function ($resolve) {
134+
Loop::addTimer(0.02, function () use ($resolve) {
135+
$resolve(2);
136+
});
137+
});
138+
139+
$ticks = 0;
140+
141+
// Loop will execute this tick first
142+
Loop::futureTick(function () use (&$ticks) {
143+
++$ticks;
144+
// This timer will never finish because Loop gets stopped by await
145+
// Loop needs to be manually started again to finish this timer
146+
Loop::addTimer(0.04, function () use (&$ticks) {
147+
++$ticks;
148+
});
149+
});
150+
151+
// await stops the loop when promise resolves after 0.02s
152+
Loop::futureTick(function () use (&$promise){
153+
React\Async\await($promise);
154+
});
155+
156+
Loop::run();
157+
158+
// This bahvior exists in v2 & v3 of async, we recommend to use fibers in v4 (PHP>=8.1)
159+
$this->assertEquals(1, $ticks);
160+
}
161+
162+
public function testAwaitWithAlreadyRejectedPromiseWillReturnWithoutStoppingLoop()
163+
{
164+
$ticks = 0;
165+
166+
$promise = new Promise(function ($_, $reject) {
167+
throw new \Exception();
168+
});
169+
170+
// Loop will execute this tick first
171+
Loop::futureTick(function () use (&$ticks) {
172+
++$ticks;
173+
// Loop will execute this tick third
174+
Loop::futureTick(function () use (&$ticks) {
175+
++$ticks;
176+
});
177+
});
178+
179+
// Loop will execute this tick second
180+
Loop::futureTick(function () use (&$promise){
181+
try {
182+
// await won't stop the loop if promise already rejected -> third tick will trigger
183+
React\Async\await($promise);
184+
} catch (\Exception $e) {
185+
// no-op
186+
}
187+
});
188+
189+
Loop::run();
190+
191+
$this->assertEquals(2, $ticks);
192+
}
193+
194+
public function testAwaitWithPendingPromiseThatWillRejectWillStopLoopBeforeLastTimerFinishes()
195+
{
196+
$promise = new Promise(function ($_, $reject) {
197+
Loop::addTimer(0.02, function () use (&$reject) {
198+
$reject(new \Exception());
199+
});
200+
});
201+
202+
$ticks = 0;
203+
204+
// Loop will execute this tick first
205+
Loop::futureTick(function () use (&$ticks) {
206+
++$ticks;
207+
// This timer will never finish because Loop gets stopped by await
208+
// Loop needs to be manually started again to finish this timer
209+
Loop::addTimer(0.04, function () use (&$ticks) {
210+
++$ticks;
211+
});
212+
});
213+
214+
// Loop will execute this tick second
215+
// await stops the loop when promise rejects after 0.02s
216+
Loop::futureTick(function () use (&$promise){
217+
try {
218+
React\Async\await($promise);
219+
} catch (\Exception $e) {
220+
// no-op
221+
}
222+
});
223+
224+
Loop::run();
225+
226+
// This bahvior exists in v2 & v3 of async, we recommend to use fibers in v4 (PHP>=8.1)
227+
$this->assertEquals(1, $ticks);
228+
}
229+
87230
public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise()
88231
{
89232
if (class_exists('React\Promise\When')) {

0 commit comments

Comments
 (0)