Skip to content

Commit 554fc0e

Browse files
committed
Use our own deffered system, simpler and less buggy
1 parent 8bbef55 commit 554fc0e

File tree

3 files changed

+86
-59
lines changed

3 files changed

+86
-59
lines changed

src/Client.php

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,45 +93,42 @@ public function sendRequest(RequestInterface $request)
9393
public function sendAsyncRequest(RequestInterface $request)
9494
{
9595
$reactRequest = $this->buildReactRequest($request);
96-
$deferred = new Deferred();
96+
$promise = new Promise($this->loop);
9797

98-
$reactRequest->on('error', function (\Exception $error) use ($deferred, $request) {
99-
$deferred->reject(new RequestException(
98+
$reactRequest->on('error', function (\Exception $error) use ($promise, $request) {
99+
$promise->reject(new RequestException(
100100
$error->getMessage(),
101101
$request,
102102
$error
103103
));
104104
});
105105

106-
$reactRequest->on('response', function (ReactResponse $reactResponse = null) use ($deferred, $reactRequest, $request) {
106+
$reactRequest->on('response', function (ReactResponse $reactResponse = null) use ($promise, $request) {
107107
$bodyStream = $this->streamFactory->createStream();
108108
$reactResponse->on('data', function ($data) use (&$bodyStream) {
109109
$bodyStream->write((string) $data);
110110
});
111111

112-
$reactResponse->on('end', function (\Exception $error = null) use ($deferred, $request, $reactResponse, &$bodyStream) {
112+
$reactResponse->on('end', function (\Exception $error = null) use ($promise, $request, $reactResponse, &$bodyStream) {
113113
$response = $this->buildResponse(
114114
$reactResponse,
115115
$bodyStream
116116
);
117117
if (null !== $error) {
118-
$deferred->reject(new HttpException(
118+
$promise->reject(new HttpException(
119119
$error->getMessage(),
120120
$request,
121121
$response,
122122
$error
123123
));
124124
} else {
125-
$deferred->resolve($response);
125+
$promise->resolve($response);
126126
}
127127
});
128128
});
129129

130130
$reactRequest->end((string) $request->getBody());
131131

132-
$promise = new Promise($deferred->promise());
133-
$promise->setLoop($this->loop);
134-
135132
return $promise;
136133
}
137134

src/Promise.php

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Http\Adapter\React;
44

55
use React\EventLoop\LoopInterface;
6-
use React\Promise\PromiseInterface as ReactPromise;
76
use Http\Client\Exception;
87
use Http\Promise\Promise as HttpPromise;
98
use Psr\Http\Message\ResponseInterface;
@@ -12,6 +11,8 @@
1211
* React promise adapter implementation.
1312
*
1413
* @author Stéphane Hulard <stephane@hlrd.me>
14+
*
15+
* @internal
1516
*/
1617
class Promise implements HttpPromise
1718
{
@@ -22,13 +23,6 @@ class Promise implements HttpPromise
2223
*/
2324
private $state = HttpPromise::PENDING;
2425

25-
/**
26-
* Adapted React promise.
27-
*
28-
* @var ReactPromise
29-
*/
30-
private $promise;
31-
3226
/**
3327
* PSR7 received response.
3428
*
@@ -43,31 +37,26 @@ class Promise implements HttpPromise
4337
*/
4438
private $exception;
4539

40+
/**
41+
* @var callable|null
42+
*/
43+
private $onFulfilled;
44+
45+
/**
46+
* @var callable|null
47+
*/
48+
private $onRejected;
49+
4650
/**
4751
* React Event Loop used for synchronous processing.
4852
*
4953
* @var LoopInterface
5054
*/
5155
private $loop;
5256

53-
/**
54-
* Initialize the promise.
55-
*
56-
* @param ReactPromise $promise
57-
*/
58-
public function __construct(ReactPromise $promise)
57+
public function __construct(LoopInterface $loop)
5958
{
60-
$promise->then(
61-
function (ResponseInterface $response) {
62-
$this->state = HttpPromise::FULFILLED;
63-
$this->response = $response;
64-
},
65-
function (Exception $error) {
66-
$this->state = HttpPromise::REJECTED;
67-
$this->exception = $error;
68-
}
69-
);
70-
$this->promise = $promise;
59+
$this->loop = $loop;
7160
}
7261

7362
/**
@@ -80,49 +69,92 @@ function (Exception $error) {
8069
*/
8170
public function then(callable $onFulfilled = null, callable $onRejected = null)
8271
{
83-
$this->promise->then(function () use ($onFulfilled) {
84-
if (null !== $onFulfilled) {
85-
call_user_func($onFulfilled, $this->response);
72+
$newPromise = new Promise($this->loop);
73+
74+
$onFulfilled = $onFulfilled !== null ? $onFulfilled : function (ResponseInterface $response) {
75+
return $response;
76+
};
77+
78+
$onRejected = $onRejected !== null ? $onRejected : function (Exception $exception) {
79+
throw $exception;
80+
};
81+
82+
$this->onFulfilled = function(ResponseInterface $response) use ($onFulfilled, $newPromise) {
83+
try {
84+
$newPromise->resolve($onFulfilled($response));
85+
} catch (Exception $exception) {
86+
$newPromise->reject($exception);
8687
}
87-
}, function () use ($onRejected) {
88-
if (null !== $onRejected) {
89-
call_user_func($onRejected, $this->exception);
88+
};
89+
90+
$this->onRejected = function(Exception $exception) use ($onRejected, $newPromise) {
91+
try {
92+
$newPromise->resolve($onRejected($exception));
93+
} catch (Exception $exception) {
94+
$newPromise->reject($exception);
9095
}
91-
});
96+
};
9297

93-
return $this;
98+
return $newPromise;
9499
}
95100

96101
/**
97-
* {@inheritdoc}
102+
* Resolve this promise
103+
*
104+
* @param ResponseInterface $response
105+
*
106+
* @internal
98107
*/
99-
public function getState()
108+
public function resolve(ResponseInterface $response)
100109
{
101-
return $this->state;
110+
if ($this->state !== HttpPromise::PENDING) {
111+
throw new \RuntimeException('Promise is already resolved');
112+
}
113+
114+
$this->state = HttpPromise::FULFILLED;
115+
$this->response = $response;
116+
$onFulfilled = $this->onFulfilled;
117+
118+
if (null !== $onFulfilled) {
119+
$onFulfilled($response);
120+
}
102121
}
103122

104123
/**
105-
* Set EventLoop used for synchronous processing.
124+
* Reject this promise
106125
*
107-
* @param LoopInterface $loop
126+
* @param Exception $exception
108127
*
109-
* @return Promise
128+
* @internal
110129
*/
111-
public function setLoop(LoopInterface $loop)
130+
public function reject(Exception $exception)
112131
{
113-
$this->loop = $loop;
132+
if ($this->state !== HttpPromise::PENDING) {
133+
throw new \RuntimeException('Promise is already resolved');
134+
}
135+
136+
$this->state = HttpPromise::REJECTED;
137+
$this->exception = $exception;
138+
$onRejected = $this->onRejected;
114139

115-
return $this;
140+
if (null !== $onRejected) {
141+
$onRejected($exception);
142+
}
143+
}
144+
145+
/**
146+
* {@inheritdoc}
147+
*/
148+
public function getState()
149+
{
150+
return $this->state;
116151
}
117152

118153
/**
119154
* {@inheritdoc}
120155
*/
121156
public function wait($unwrap = true)
122157
{
123-
if (null === $this->loop) {
124-
throw new \LogicException('You must set the loop before wait!');
125-
}
126158
while (HttpPromise::PENDING === $this->getState()) {
127159
$this->loop->tick();
128160
}

tests/PromiseTest.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,14 @@ public function setUp()
1919

2020
public function testChain()
2121
{
22-
$deferred = new Deferred();
23-
$promise = new Promise($deferred->promise());
24-
$promise->setLoop($this->loop);
22+
$promise = new Promise($this->loop);
2523
$response = new Response(200);
2624

2725
$lastPromise = $promise->then(function (Response $response) {
2826
return $response->withStatus(300);
2927
});
3028

31-
$deferred->resolve($response);
29+
$promise->resolve($response);
3230
$updatedResponse = $lastPromise->wait();
3331

3432
self::assertEquals(200, $response->getStatusCode());

0 commit comments

Comments
 (0)