Skip to content

Commit 95786ff

Browse files
committed
Merge remote-tracking branch 'origin/lazy-promise'
* origin/lazy-promise: Add When::lazy() to readme and changelog Add tests for When::lazy Add test for factory throwing an exception Add lazy promise
2 parents 8573049 + 109415f commit 95786ff

6 files changed

Lines changed: 176 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55

66
* Trigger PHP errors when invalid callback is passed.
77
* Fully resolve rejection value before calling rejection handler.
8+
* Add When::lazy() to create lazy promises which will be initialized once a
9+
consumer calls the then() method.
810

911
* 1.0.3 (2012-11-17)
1012

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Table of Contents
2626
* [When::reduce()](#whenreduce)
2727
* [When::resolve()](#whenresolve)
2828
* [When::reject()](#whenreject)
29+
* [When::lazy()](#whenlazy)
2930
* [Promisor](#promisor)
3031
4. [Examples](#examples)
3132
* [How to use Deferred](#how-to-use-deferred)
@@ -282,6 +283,31 @@ This can be useful in situations where you need to reject a Promise without
282283
throwing an exception. For example, it allows you to propagate a rejection with
283284
the value of another Promise.
284285

286+
#### When::lazy()
287+
288+
``` php
289+
$promise = React\Promise\When::lazy(callable $factory);
290+
```
291+
292+
Creates a Promise which will be lazily initialized by `$factory` once a consumer
293+
calls the `then()` method.
294+
295+
```php
296+
$factory = function () {
297+
$deferred = new React\Promise\Deferred();
298+
299+
// Do some heavy stuff here and resolve the Deferred once completed
300+
301+
return $deferred->promise();
302+
};
303+
304+
$promise = React\Promise\When::lazy($factory);
305+
306+
// $factory will only be executed once we call then()
307+
$promise->then(function ($value) {
308+
});
309+
```
310+
285311
### Promisor
286312

287313
The `React\Promise\PromisorInterface` provides a common interface for objects

src/React/Promise/LazyPromise.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
class LazyPromise implements PromiseInterface
6+
{
7+
private $factory;
8+
private $promise;
9+
10+
public function __construct($factory)
11+
{
12+
$this->factory = $factory;
13+
}
14+
15+
public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null)
16+
{
17+
if (null === $this->promise) {
18+
try {
19+
$this->promise = Util::promiseFor(call_user_func($this->factory));
20+
} catch (\Exception $exception) {
21+
$this->promise = new RejectedPromise($exception);
22+
}
23+
}
24+
25+
return $this->promise->then($fulfilledHandler, $errorHandler, $progressHandler);
26+
}
27+
}

src/React/Promise/When.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public static function reject($promiseOrValue = null)
1414
return Util::rejectedPromiseFor($promiseOrValue);
1515
}
1616

17+
public static function lazy($factory)
18+
{
19+
return new LazyPromise($factory);
20+
}
21+
1722
public static function all($promisesOrValues, $fulfilledHandler = null, $errorHandler = null, $progressHandler = null)
1823
{
1924
$promise = static::map($promisesOrValues, function ($val) {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
/**
6+
* @group Promise
7+
* @group LazyPromise
8+
*/
9+
class LazyPromiseTest extends TestCase
10+
{
11+
/** @test */
12+
public function shouldNotCallFactoryIfThenIsNotInvoked()
13+
{
14+
$factory = $this->createCallableMock();
15+
$factory
16+
->expects($this->never())
17+
->method('__invoke');
18+
19+
new LazyPromise($factory);
20+
}
21+
22+
/** @test */
23+
public function shouldCallFactoryIfThenIsInvoked()
24+
{
25+
$factory = $this->createCallableMock();
26+
$factory
27+
->expects($this->once())
28+
->method('__invoke');
29+
30+
$p = new LazyPromise($factory);
31+
$p->then();
32+
}
33+
34+
/** @test */
35+
public function shouldReturnPromiseFromFactory()
36+
{
37+
$factory = $this->createCallableMock();
38+
$factory
39+
->expects($this->once())
40+
->method('__invoke')
41+
->will($this->returnValue(new FulfilledPromise(1)));
42+
43+
$fulfilledHandler = $this->createCallableMock();
44+
$fulfilledHandler
45+
->expects($this->once())
46+
->method('__invoke')
47+
->with($this->identicalTo(1));
48+
49+
$p = new LazyPromise($factory);
50+
51+
$p->then($fulfilledHandler);
52+
}
53+
54+
/** @test */
55+
public function shouldReturnPromiseIfFactoryReturnsNull()
56+
{
57+
$factory = $this->createCallableMock();
58+
$factory
59+
->expects($this->once())
60+
->method('__invoke')
61+
->will($this->returnValue(null));
62+
63+
$p = new LazyPromise($factory);
64+
$this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then());
65+
}
66+
67+
/** @test */
68+
public function shouldReturnRejectedPromiseIfFactoryThrowsException()
69+
{
70+
$exception = new \Exception();
71+
72+
$factory = $this->createCallableMock();
73+
$factory
74+
->expects($this->once())
75+
->method('__invoke')
76+
->will($this->throwException($exception));
77+
78+
$errorHandler = $this->createCallableMock();
79+
$errorHandler
80+
->expects($this->once())
81+
->method('__invoke')
82+
->with($this->identicalTo($exception));
83+
84+
$p = new LazyPromise($factory);
85+
86+
$p->then($this->expectCallableNever(), $errorHandler);
87+
}
88+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
/**
6+
* @group When
7+
* @group WhenLazy
8+
*/
9+
class WhenLazyTest extends TestCase
10+
{
11+
/** @test */
12+
public function shouldReturnALazyPromise()
13+
{
14+
$this->assertInstanceOf('React\\Promise\\PromiseInterface', When::lazy(function () {}));
15+
}
16+
17+
/** @test */
18+
public function shouldCallFactoryIfThenIsInvoked()
19+
{
20+
$factory = $this->createCallableMock();
21+
$factory
22+
->expects($this->once())
23+
->method('__invoke');
24+
25+
When::lazy($factory)
26+
->then();
27+
}
28+
}

0 commit comments

Comments
 (0)