File tree Expand file tree Collapse file tree 2 files changed +43
-14
lines changed Expand file tree Collapse file tree 2 files changed +43
-14
lines changed Original file line number Diff line number Diff line change @@ -164,17 +164,6 @@ private function reject($reason = null)
164
164
$ this ->settle (reject ($ reason ));
165
165
}
166
166
167
- private function notify ($ update = null )
168
- {
169
- if (null !== $ this ->result ) {
170
- return ;
171
- }
172
-
173
- foreach ($ this ->progressHandlers as $ handler ) {
174
- $ handler ($ update );
175
- }
176
- }
177
-
178
167
private function settle (ExtendedPromiseInterface $ promise )
179
168
{
180
169
$ promise = $ this ->unwrap ($ promise );
@@ -239,16 +228,23 @@ private function call(callable $callback)
239
228
if ($ args === 0 ) {
240
229
$ callback ();
241
230
} else {
231
+ // Store a reference to all progress handlers (will be cleared when settled)
232
+ // This way, we can use a static progress callback that is not bound to this promise instance.
233
+ // This helps avoiding garbage cycles if the callback creates an Exception.
234
+ $ progress =& $ this ->progressHandlers ;
235
+
242
236
$ callback (
243
237
function ($ value = null ) {
244
238
$ this ->resolve ($ value );
245
239
},
246
240
function ($ reason = null ) {
247
241
$ this ->reject ($ reason );
248
242
},
249
- function ($ update = null ) {
250
- $ this ->notify ($ update );
251
- }
243
+ \Closure::bind (function ($ update = null ) use (&$ progress ) {
244
+ foreach ($ progress as $ handler ) {
245
+ $ handler ($ update );
246
+ }
247
+ }, null )
252
248
);
253
249
}
254
250
} catch (\Throwable $ e ) {
Original file line number Diff line number Diff line change @@ -60,6 +60,39 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfResolverThrowsExceptio
60
60
$ this ->assertSame (0 , gc_collect_cycles ());
61
61
}
62
62
63
+ /**
64
+ * test that checks number of garbage cycles after throwing from a resolver
65
+ * that has its arguments explicitly set to null (reassigned arguments only
66
+ * show up in the stack trace in PHP 7, so we can't test this on legacy PHP)
67
+ *
68
+ * @test
69
+ * @requires PHP 7
70
+ * @link https://3v4l.org/OiDr4
71
+ */
72
+ public function shouldRejectWithoutCreatingGarbageCyclesIfResolverThrowsExceptionWithResolveAndRejectUnset ()
73
+ {
74
+ gc_collect_cycles ();
75
+ $ promise = new Promise (function ($ resolve , $ reject ) {
76
+ $ resolve = $ reject = null ;
77
+ throw new \Exception ('foo ' );
78
+ });
79
+ unset($ promise );
80
+
81
+ $ this ->assertSame (0 , gc_collect_cycles ());
82
+ }
83
+
84
+ /** @test */
85
+ public function shouldIgnoreNotifyAfterReject ()
86
+ {
87
+ $ promise = new Promise (function () { }, function ($ resolve , $ reject , $ notify ) {
88
+ $ reject (new \Exception ('foo ' ));
89
+ $ notify (42 );
90
+ });
91
+
92
+ $ promise ->then (null , null , $ this ->expectCallableNever ());
93
+ $ promise ->cancel ();
94
+ }
95
+
63
96
/** @test */
64
97
public function shouldFulfillIfFullfilledWithSimplePromise ()
65
98
{
You can’t perform that action at this time.
0 commit comments