functionsTest.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. <?php
  2. namespace GuzzleHttp\Promise\Tests;
  3. use GuzzleHttp\Promise as P;
  4. use GuzzleHttp\Promise\AggregateException;
  5. use GuzzleHttp\Promise\FulfilledPromise;
  6. use GuzzleHttp\Promise\Promise;
  7. use GuzzleHttp\Promise\PromiseInterface;
  8. use GuzzleHttp\Promise\RejectedPromise;
  9. use GuzzleHttp\Promise\RejectionException;
  10. use GuzzleHttp\Promise\TaskQueue;
  11. use PHPUnit\Framework\TestCase;
  12. class FunctionsTest extends TestCase
  13. {
  14. public function testCreatesPromiseForValue()
  15. {
  16. $p = \GuzzleHttp\Promise\promise_for('foo');
  17. $this->assertInstanceOf(FulfilledPromise::class, $p);
  18. }
  19. public function testReturnsPromiseForPromise()
  20. {
  21. $p = new Promise();
  22. $this->assertSame($p, \GuzzleHttp\Promise\promise_for($p));
  23. }
  24. public function testReturnsPromiseForThennable()
  25. {
  26. $p = new Thennable();
  27. $wrapped = \GuzzleHttp\Promise\promise_for($p);
  28. $this->assertNotSame($p, $wrapped);
  29. $this->assertInstanceOf(PromiseInterface::class, $wrapped);
  30. $p->resolve('foo');
  31. P\queue()->run();
  32. $this->assertEquals('foo', $wrapped->wait());
  33. }
  34. public function testReturnsRejection()
  35. {
  36. $p = \GuzzleHttp\Promise\rejection_for('fail');
  37. $this->assertInstanceOf(RejectedPromise::class, $p);
  38. $this->assertEquals('fail', $this->readAttribute($p, 'reason'));
  39. }
  40. public function testReturnsPromisesAsIsInRejectionFor()
  41. {
  42. $a = new Promise();
  43. $b = \GuzzleHttp\Promise\rejection_for($a);
  44. $this->assertSame($a, $b);
  45. }
  46. public function testWaitsOnAllPromisesIntoArray()
  47. {
  48. $e = new \Exception();
  49. $a = new Promise(function () use (&$a) { $a->resolve('a'); });
  50. $b = new Promise(function () use (&$b) { $b->reject('b'); });
  51. $c = new Promise(function () use (&$c, $e) { $c->reject($e); });
  52. $results = \GuzzleHttp\Promise\inspect_all([$a, $b, $c]);
  53. $this->assertEquals([
  54. ['state' => 'fulfilled', 'value' => 'a'],
  55. ['state' => 'rejected', 'reason' => 'b'],
  56. ['state' => 'rejected', 'reason' => $e]
  57. ], $results);
  58. }
  59. /**
  60. * @expectedException \GuzzleHttp\Promise\RejectionException
  61. */
  62. public function testUnwrapsPromisesWithNoDefaultAndFailure()
  63. {
  64. $promises = [new FulfilledPromise('a'), new Promise()];
  65. \GuzzleHttp\Promise\unwrap($promises);
  66. }
  67. public function testUnwrapsPromisesWithNoDefault()
  68. {
  69. $promises = [new FulfilledPromise('a')];
  70. $this->assertEquals(['a'], \GuzzleHttp\Promise\unwrap($promises));
  71. }
  72. public function testUnwrapsPromisesWithKeys()
  73. {
  74. $promises = [
  75. 'foo' => new FulfilledPromise('a'),
  76. 'bar' => new FulfilledPromise('b'),
  77. ];
  78. $this->assertEquals([
  79. 'foo' => 'a',
  80. 'bar' => 'b'
  81. ], \GuzzleHttp\Promise\unwrap($promises));
  82. }
  83. public function testAllAggregatesSortedArray()
  84. {
  85. $a = new Promise();
  86. $b = new Promise();
  87. $c = new Promise();
  88. $d = \GuzzleHttp\Promise\all([$a, $b, $c]);
  89. $b->resolve('b');
  90. $a->resolve('a');
  91. $c->resolve('c');
  92. $d->then(
  93. function ($value) use (&$result) { $result = $value; },
  94. function ($reason) use (&$result) { $result = $reason; }
  95. );
  96. P\queue()->run();
  97. $this->assertEquals(['a', 'b', 'c'], $result);
  98. }
  99. public function testPromisesDynamicallyAddedToStack()
  100. {
  101. $promises = new \ArrayIterator();
  102. $counter = 0;
  103. $promises['a'] = new FulfilledPromise('a');
  104. $promises['b'] = $promise = new Promise(function () use (&$promise, &$promises, &$counter) {
  105. $counter++; // Make sure the wait function is called only once
  106. $promise->resolve('b');
  107. $promises['c'] = $subPromise = new Promise(function () use (&$subPromise) {
  108. $subPromise->resolve('c');
  109. });
  110. });
  111. $result = \GuzzleHttp\Promise\all($promises, true)->wait();
  112. $this->assertCount(3, $promises);
  113. $this->assertCount(3, $result);
  114. $this->assertEquals($result['c'], 'c');
  115. $this->assertSame(1, $counter);
  116. }
  117. public function testAllThrowsWhenAnyRejected()
  118. {
  119. $a = new Promise();
  120. $b = new Promise();
  121. $c = new Promise();
  122. $d = \GuzzleHttp\Promise\all([$a, $b, $c]);
  123. $b->resolve('b');
  124. $a->reject('fail');
  125. $c->resolve('c');
  126. $d->then(
  127. function ($value) use (&$result) { $result = $value; },
  128. function ($reason) use (&$result) { $result = $reason; }
  129. );
  130. P\queue()->run();
  131. $this->assertEquals('fail', $result);
  132. }
  133. public function testSomeAggregatesSortedArrayWithMax()
  134. {
  135. $a = new Promise();
  136. $b = new Promise();
  137. $c = new Promise();
  138. $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]);
  139. $b->resolve('b');
  140. $c->resolve('c');
  141. $a->resolve('a');
  142. $d->then(function ($value) use (&$result) { $result = $value; });
  143. P\queue()->run();
  144. $this->assertEquals(['b', 'c'], $result);
  145. }
  146. public function testSomeRejectsWhenTooManyRejections()
  147. {
  148. $a = new Promise();
  149. $b = new Promise();
  150. $d = \GuzzleHttp\Promise\some(2, [$a, $b]);
  151. $a->reject('bad');
  152. $b->resolve('good');
  153. P\queue()->run();
  154. $this->assertEquals($a::REJECTED, $d->getState());
  155. $d->then(null, function ($reason) use (&$called) {
  156. $called = $reason;
  157. });
  158. P\queue()->run();
  159. $this->assertInstanceOf(AggregateException::class, $called);
  160. $this->assertContains('bad', $called->getReason());
  161. }
  162. public function testCanWaitUntilSomeCountIsSatisfied()
  163. {
  164. $a = new Promise(function () use (&$a) { $a->resolve('a'); });
  165. $b = new Promise(function () use (&$b) { $b->resolve('b'); });
  166. $c = new Promise(function () use (&$c) { $c->resolve('c'); });
  167. $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]);
  168. $this->assertEquals(['a', 'b'], $d->wait());
  169. }
  170. /**
  171. * @expectedException \GuzzleHttp\Promise\AggregateException
  172. * @expectedExceptionMessage Not enough promises to fulfill count
  173. */
  174. public function testThrowsIfImpossibleToWaitForSomeCount()
  175. {
  176. $a = new Promise(function () use (&$a) { $a->resolve('a'); });
  177. $d = \GuzzleHttp\Promise\some(2, [$a]);
  178. $d->wait();
  179. }
  180. /**
  181. * @expectedException \GuzzleHttp\Promise\AggregateException
  182. * @expectedExceptionMessage Not enough promises to fulfill count
  183. */
  184. public function testThrowsIfResolvedWithoutCountTotalResults()
  185. {
  186. $a = new Promise();
  187. $b = new Promise();
  188. $d = \GuzzleHttp\Promise\some(3, [$a, $b]);
  189. $a->resolve('a');
  190. $b->resolve('b');
  191. $d->wait();
  192. }
  193. public function testAnyReturnsFirstMatch()
  194. {
  195. $a = new Promise();
  196. $b = new Promise();
  197. $c = \GuzzleHttp\Promise\any([$a, $b]);
  198. $b->resolve('b');
  199. $a->resolve('a');
  200. //P\queue()->run();
  201. //$this->assertEquals('fulfilled', $c->getState());
  202. $c->then(function ($value) use (&$result) { $result = $value; });
  203. P\queue()->run();
  204. $this->assertEquals('b', $result);
  205. }
  206. public function testSettleFulfillsWithFulfilledAndRejected()
  207. {
  208. $a = new Promise();
  209. $b = new Promise();
  210. $c = new Promise();
  211. $d = \GuzzleHttp\Promise\settle([$a, $b, $c]);
  212. $b->resolve('b');
  213. $c->resolve('c');
  214. $a->reject('a');
  215. P\queue()->run();
  216. $this->assertEquals('fulfilled', $d->getState());
  217. $d->then(function ($value) use (&$result) { $result = $value; });
  218. P\queue()->run();
  219. $this->assertEquals([
  220. ['state' => 'rejected', 'reason' => 'a'],
  221. ['state' => 'fulfilled', 'value' => 'b'],
  222. ['state' => 'fulfilled', 'value' => 'c']
  223. ], $result);
  224. }
  225. public function testCanInspectFulfilledPromise()
  226. {
  227. $p = new FulfilledPromise('foo');
  228. $this->assertEquals([
  229. 'state' => 'fulfilled',
  230. 'value' => 'foo'
  231. ], \GuzzleHttp\Promise\inspect($p));
  232. }
  233. public function testCanInspectRejectedPromise()
  234. {
  235. $p = new RejectedPromise('foo');
  236. $this->assertEquals([
  237. 'state' => 'rejected',
  238. 'reason' => 'foo'
  239. ], \GuzzleHttp\Promise\inspect($p));
  240. }
  241. public function testCanInspectRejectedPromiseWithNormalException()
  242. {
  243. $e = new \Exception('foo');
  244. $p = new RejectedPromise($e);
  245. $this->assertEquals([
  246. 'state' => 'rejected',
  247. 'reason' => $e
  248. ], \GuzzleHttp\Promise\inspect($p));
  249. }
  250. public function testCallsEachLimit()
  251. {
  252. $p = new Promise();
  253. $aggregate = \GuzzleHttp\Promise\each_limit($p, 2);
  254. $p->resolve('a');
  255. P\queue()->run();
  256. $this->assertEquals($p::FULFILLED, $aggregate->getState());
  257. }
  258. public function testEachLimitAllRejectsOnFailure()
  259. {
  260. $p = [new FulfilledPromise('a'), new RejectedPromise('b')];
  261. $aggregate = \GuzzleHttp\Promise\each_limit_all($p, 2);
  262. P\queue()->run();
  263. $this->assertEquals(PromiseInterface::REJECTED, $aggregate->getState());
  264. $result = \GuzzleHttp\Promise\inspect($aggregate);
  265. $this->assertEquals('b', $result['reason']);
  266. }
  267. public function testIterForReturnsIterator()
  268. {
  269. $iter = new \ArrayIterator();
  270. $this->assertSame($iter, \GuzzleHttp\Promise\iter_for($iter));
  271. }
  272. public function testKnowsIfFulfilled()
  273. {
  274. $p = new FulfilledPromise(null);
  275. $this->assertTrue(P\is_fulfilled($p));
  276. $this->assertFalse(P\is_rejected($p));
  277. }
  278. public function testKnowsIfRejected()
  279. {
  280. $p = new RejectedPromise(null);
  281. $this->assertTrue(P\is_rejected($p));
  282. $this->assertFalse(P\is_fulfilled($p));
  283. }
  284. public function testKnowsIfSettled()
  285. {
  286. $p = new RejectedPromise(null);
  287. $this->assertTrue(P\is_settled($p));
  288. $p = new Promise();
  289. $this->assertFalse(P\is_settled($p));
  290. }
  291. public function testReturnsTrampoline()
  292. {
  293. $this->assertInstanceOf(TaskQueue::class, P\queue());
  294. $this->assertSame(P\queue(), P\queue());
  295. }
  296. public function testCanScheduleThunk()
  297. {
  298. $tramp = P\queue();
  299. $promise = P\task(function () { return 'Hi!'; });
  300. $c = null;
  301. $promise->then(function ($v) use (&$c) { $c = $v; });
  302. $this->assertNull($c);
  303. $tramp->run();
  304. $this->assertEquals('Hi!', $c);
  305. }
  306. public function testCanScheduleThunkWithRejection()
  307. {
  308. $tramp = P\queue();
  309. $promise = P\task(function () { throw new \Exception('Hi!'); });
  310. $c = null;
  311. $promise->otherwise(function ($v) use (&$c) { $c = $v; });
  312. $this->assertNull($c);
  313. $tramp->run();
  314. $this->assertEquals('Hi!', $c->getMessage());
  315. }
  316. public function testCanScheduleThunkWithWait()
  317. {
  318. $tramp = P\queue();
  319. $promise = P\task(function () { return 'a'; });
  320. $this->assertEquals('a', $promise->wait());
  321. $tramp->run();
  322. }
  323. public function testYieldsFromCoroutine()
  324. {
  325. $promise = P\coroutine(function () {
  326. $value = (yield new P\FulfilledPromise('a'));
  327. yield $value . 'b';
  328. });
  329. $promise->then(function ($value) use (&$result) { $result = $value; });
  330. P\queue()->run();
  331. $this->assertEquals('ab', $result);
  332. }
  333. public function testCanCatchExceptionsInCoroutine()
  334. {
  335. $promise = P\coroutine(function () {
  336. try {
  337. yield new P\RejectedPromise('a');
  338. $this->fail('Should have thrown into the coroutine!');
  339. } catch (RejectionException $e) {
  340. $value = (yield new P\FulfilledPromise($e->getReason()));
  341. yield $value . 'b';
  342. }
  343. });
  344. $promise->then(function ($value) use (&$result) { $result = $value; });
  345. P\queue()->run();
  346. $this->assertEquals(PromiseInterface::FULFILLED, $promise->getState());
  347. $this->assertEquals('ab', $result);
  348. }
  349. public function testRejectsParentExceptionWhenException()
  350. {
  351. $promise = P\coroutine(function () {
  352. yield new P\FulfilledPromise(0);
  353. throw new \Exception('a');
  354. });
  355. $promise->then(
  356. function () { $this->fail(); },
  357. function ($reason) use (&$result) { $result = $reason; }
  358. );
  359. P\queue()->run();
  360. $this->assertInstanceOf(\Exception::class, $result);
  361. $this->assertEquals('a', $result->getMessage());
  362. }
  363. public function testCanRejectFromRejectionCallback()
  364. {
  365. $promise = P\coroutine(function () {
  366. yield new P\FulfilledPromise(0);
  367. yield new P\RejectedPromise('no!');
  368. });
  369. $promise->then(
  370. function () { $this->fail(); },
  371. function ($reason) use (&$result) { $result = $reason; }
  372. );
  373. P\queue()->run();
  374. $this->assertInstanceOf(RejectionException::class, $result);
  375. $this->assertEquals('no!', $result->getReason());
  376. }
  377. public function testCanAsyncReject()
  378. {
  379. $rej = new P\Promise();
  380. $promise = P\coroutine(function () use ($rej) {
  381. yield new P\FulfilledPromise(0);
  382. yield $rej;
  383. });
  384. $promise->then(
  385. function () { $this->fail(); },
  386. function ($reason) use (&$result) { $result = $reason; }
  387. );
  388. $rej->reject('no!');
  389. P\queue()->run();
  390. $this->assertInstanceOf(RejectionException::class, $result);
  391. $this->assertEquals('no!', $result->getReason());
  392. }
  393. public function testCanCatchAndThrowOtherException()
  394. {
  395. $promise = P\coroutine(function () {
  396. try {
  397. yield new P\RejectedPromise('a');
  398. $this->fail('Should have thrown into the coroutine!');
  399. } catch (RejectionException $e) {
  400. throw new \Exception('foo');
  401. }
  402. });
  403. $promise->otherwise(function ($value) use (&$result) { $result = $value; });
  404. P\queue()->run();
  405. $this->assertEquals(PromiseInterface::REJECTED, $promise->getState());
  406. $this->assertContains('foo', $result->getMessage());
  407. }
  408. public function testCanCatchAndYieldOtherException()
  409. {
  410. $promise = P\coroutine(function () {
  411. try {
  412. yield new P\RejectedPromise('a');
  413. $this->fail('Should have thrown into the coroutine!');
  414. } catch (RejectionException $e) {
  415. yield new P\RejectedPromise('foo');
  416. }
  417. });
  418. $promise->otherwise(function ($value) use (&$result) { $result = $value; });
  419. P\queue()->run();
  420. $this->assertEquals(PromiseInterface::REJECTED, $promise->getState());
  421. $this->assertContains('foo', $result->getMessage());
  422. }
  423. public function createLotsOfSynchronousPromise()
  424. {
  425. return P\coroutine(function () {
  426. $value = 0;
  427. for ($i = 0; $i < 1000; $i++) {
  428. $value = (yield new P\FulfilledPromise($i));
  429. }
  430. yield $value;
  431. });
  432. }
  433. public function testLotsOfSynchronousDoesNotBlowStack()
  434. {
  435. $promise = $this->createLotsOfSynchronousPromise();
  436. $promise->then(function ($v) use (&$r) { $r = $v; });
  437. P\queue()->run();
  438. $this->assertEquals(999, $r);
  439. }
  440. public function testLotsOfSynchronousWaitDoesNotBlowStack()
  441. {
  442. $promise = $this->createLotsOfSynchronousPromise();
  443. $promise->then(function ($v) use (&$r) { $r = $v; });
  444. $this->assertEquals(999, $promise->wait());
  445. $this->assertEquals(999, $r);
  446. }
  447. private function createLotsOfFlappingPromise()
  448. {
  449. return P\coroutine(function () {
  450. $value = 0;
  451. for ($i = 0; $i < 1000; $i++) {
  452. try {
  453. if ($i % 2) {
  454. $value = (yield new P\FulfilledPromise($i));
  455. } else {
  456. $value = (yield new P\RejectedPromise($i));
  457. }
  458. } catch (\Exception $e) {
  459. $value = (yield new P\FulfilledPromise($i));
  460. }
  461. }
  462. yield $value;
  463. });
  464. }
  465. public function testLotsOfTryCatchingDoesNotBlowStack()
  466. {
  467. $promise = $this->createLotsOfFlappingPromise();
  468. $promise->then(function ($v) use (&$r) { $r = $v; });
  469. P\queue()->run();
  470. $this->assertEquals(999, $r);
  471. }
  472. public function testLotsOfTryCatchingWaitingDoesNotBlowStack()
  473. {
  474. $promise = $this->createLotsOfFlappingPromise();
  475. $promise->then(function ($v) use (&$r) { $r = $v; });
  476. $this->assertEquals(999, $promise->wait());
  477. $this->assertEquals(999, $r);
  478. }
  479. public function testAsyncPromisesWithCorrectlyYieldedValues()
  480. {
  481. $promises = [
  482. new P\Promise(),
  483. new P\Promise(),
  484. new P\Promise()
  485. ];
  486. $promise = P\coroutine(function () use ($promises) {
  487. $value = null;
  488. $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
  489. foreach ($promises as $idx => $p) {
  490. $value = (yield $p);
  491. $this->assertEquals($value, $idx);
  492. $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
  493. }
  494. $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
  495. yield $value;
  496. });
  497. $promises[0]->resolve(0);
  498. $promises[1]->resolve(1);
  499. $promises[2]->resolve(2);
  500. $promise->then(function ($v) use (&$r) { $r = $v; });
  501. P\queue()->run();
  502. $this->assertEquals(2, $r);
  503. }
  504. public function testYieldFinalWaitablePromise()
  505. {
  506. $p1 = new P\Promise(function () use (&$p1) {
  507. $p1->resolve('skip me');
  508. });
  509. $p2 = new P\Promise(function () use (&$p2) {
  510. $p2->resolve('hello!');
  511. });
  512. $co = P\coroutine(function() use ($p1, $p2) {
  513. yield $p1;
  514. yield $p2;
  515. });
  516. P\queue()->run();
  517. $this->assertEquals('hello!', $co->wait());
  518. }
  519. public function testCanYieldFinalPendingPromise()
  520. {
  521. $p1 = new P\Promise();
  522. $p2 = new P\Promise();
  523. $co = P\coroutine(function() use ($p1, $p2) {
  524. yield $p1;
  525. yield $p2;
  526. });
  527. $p1->resolve('a');
  528. $p2->resolve('b');
  529. $co->then(function ($value) use (&$result) { $result = $value; });
  530. P\queue()->run();
  531. $this->assertEquals('b', $result);
  532. }
  533. public function testCanNestYieldsAndFailures()
  534. {
  535. $p1 = new P\Promise();
  536. $p2 = new P\Promise();
  537. $p3 = new P\Promise();
  538. $p4 = new P\Promise();
  539. $p5 = new P\Promise();
  540. $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5) {
  541. try {
  542. yield $p1;
  543. } catch (\Exception $e) {
  544. yield $p2;
  545. try {
  546. yield $p3;
  547. yield $p4;
  548. } catch (\Exception $e) {
  549. yield $p5;
  550. }
  551. }
  552. });
  553. $p1->reject('a');
  554. $p2->resolve('b');
  555. $p3->resolve('c');
  556. $p4->reject('d');
  557. $p5->resolve('e');
  558. $co->then(function ($value) use (&$result) { $result = $value; });
  559. P\queue()->run();
  560. $this->assertEquals('e', $result);
  561. }
  562. public function testCanYieldErrorsAndSuccessesWithoutRecursion()
  563. {
  564. $promises = [];
  565. for ($i = 0; $i < 20; $i++) {
  566. $promises[] = new P\Promise();
  567. }
  568. $co = P\coroutine(function() use ($promises) {
  569. for ($i = 0; $i < 20; $i += 4) {
  570. try {
  571. yield $promises[$i];
  572. yield $promises[$i + 1];
  573. } catch (\Exception $e) {
  574. yield $promises[$i + 2];
  575. yield $promises[$i + 3];
  576. }
  577. }
  578. });
  579. for ($i = 0; $i < 20; $i += 4) {
  580. $promises[$i]->resolve($i);
  581. $promises[$i + 1]->reject($i + 1);
  582. $promises[$i + 2]->resolve($i + 2);
  583. $promises[$i + 3]->resolve($i + 3);
  584. }
  585. $co->then(function ($value) use (&$result) { $result = $value; });
  586. P\queue()->run();
  587. $this->assertEquals('19', $result);
  588. }
  589. public function testCanWaitOnPromiseAfterFulfilled()
  590. {
  591. $f = function () {
  592. static $i = 0;
  593. $i++;
  594. return $p = new P\Promise(function () use (&$p, $i) {
  595. $p->resolve($i . '-bar');
  596. });
  597. };
  598. $promises = [];
  599. for ($i = 0; $i < 20; $i++) {
  600. $promises[] = $f();
  601. }
  602. $p = P\coroutine(function () use ($promises) {
  603. yield new P\FulfilledPromise('foo!');
  604. foreach ($promises as $promise) {
  605. yield $promise;
  606. }
  607. });
  608. $this->assertEquals('20-bar', $p->wait());
  609. }
  610. public function testCanWaitOnErroredPromises()
  611. {
  612. $p1 = new P\Promise(function () use (&$p1) { $p1->reject('a'); });
  613. $p2 = new P\Promise(function () use (&$p2) { $p2->resolve('b'); });
  614. $p3 = new P\Promise(function () use (&$p3) { $p3->resolve('c'); });
  615. $p4 = new P\Promise(function () use (&$p4) { $p4->reject('d'); });
  616. $p5 = new P\Promise(function () use (&$p5) { $p5->resolve('e'); });
  617. $p6 = new P\Promise(function () use (&$p6) { $p6->reject('f'); });
  618. $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5, $p6) {
  619. try {
  620. yield $p1;
  621. } catch (\Exception $e) {
  622. yield $p2;
  623. try {
  624. yield $p3;
  625. yield $p4;
  626. } catch (\Exception $e) {
  627. yield $p5;
  628. yield $p6;
  629. }
  630. }
  631. });
  632. $res = P\inspect($co);
  633. $this->assertEquals('f', $res['reason']);
  634. }
  635. public function testCoroutineOtherwiseIntegrationTest()
  636. {
  637. $a = new P\Promise();
  638. $b = new P\Promise();
  639. $promise = P\coroutine(function () use ($a, $b) {
  640. // Execute the pool of commands concurrently, and process errors.
  641. yield $a;
  642. yield $b;
  643. })->otherwise(function (\Exception $e) {
  644. // Throw errors from the operations as a specific Multipart error.
  645. throw new \OutOfBoundsException('a', 0, $e);
  646. });
  647. $a->resolve('a');
  648. $b->reject('b');
  649. $reason = P\inspect($promise)['reason'];
  650. $this->assertInstanceOf(\OutOfBoundsException::class, $reason);
  651. $this->assertInstanceOf(RejectionException::class, $reason->getPrevious());
  652. }
  653. }