line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Promises6::Resolver; |
2
|
29
|
|
|
29
|
|
731
|
use Evo::Base -base; |
|
29
|
|
|
|
|
37
|
|
|
29
|
|
|
|
|
165
|
|
3
|
29
|
|
|
29
|
|
4383
|
use Promises6::Util ':all'; |
|
29
|
|
|
|
|
40
|
|
|
29
|
|
|
|
|
4046
|
|
4
|
29
|
|
|
29
|
|
142
|
use Scalar::Util; |
|
29
|
|
|
|
|
39
|
|
|
29
|
|
|
|
|
1177
|
|
5
|
29
|
|
|
29
|
|
111923
|
use Hash::Util::FieldHash 'fieldhash'; |
|
29
|
|
|
|
|
23498
|
|
|
29
|
|
|
|
|
16988
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# that's the harder part of specification, because some paragraphs are not so obvious |
8
|
|
|
|
|
|
|
# even JS Q doesn't implement it in the right way. But we'll take a try |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# SpecA+ .. a thenable that participates in a circular thenable chain.. |
11
|
|
|
|
|
|
|
# we do reject in cb because if somehow the same thenable won't call |
12
|
|
|
|
|
|
|
# resolve cb, than there are no infinite. Also race can call the same |
13
|
|
|
|
|
|
|
# thenable twice. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# if then will call resolve or reject with a value and then die, |
16
|
|
|
|
|
|
|
# our deferred will remain unchanged, because will be fulfilled with a value |
17
|
|
|
|
|
|
|
# but we need to track a case when then calls $resolved and die, and send_msg |
18
|
|
|
|
|
|
|
# is subclassed with next_tick. because we use the same deferred, wich |
19
|
|
|
|
|
|
|
# remains PENDING. |
20
|
|
|
|
|
|
|
# So we're going to use a counter and ignore an error if $resolve was called |
21
|
|
|
|
|
|
|
# |
22
|
|
|
|
|
|
|
# Also we have cover a case when thenable calls our callback twice, first time |
23
|
|
|
|
|
|
|
# with thenable or other promise, and second with a plane value. We should |
24
|
|
|
|
|
|
|
# ignore the second call. So we track the fact that callback was called on both |
25
|
|
|
|
|
|
|
# onResolve and onReject. see then/thenable_call_cbs_several_times.t |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
fieldhash my %THENABLES; |
29
|
|
|
|
|
|
|
has 'builder'; |
30
|
|
|
|
|
|
|
|
31
|
39
|
|
100
|
39
|
1
|
809
|
sub thenables { $THENABLES{$_[1]} //= {}; } |
32
|
|
|
|
|
|
|
|
33
|
231
|
50
|
|
231
|
1
|
4813
|
sub resolve($self, $deferred, $val) { |
|
231
|
50
|
|
|
|
432
|
|
|
231
|
|
|
|
|
242
|
|
|
231
|
|
|
|
|
195
|
|
|
231
|
|
|
|
|
212
|
|
|
231
|
|
|
|
|
203
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# not a promise, but deferred protect from mem leaks |
36
|
231
|
100
|
100
|
|
|
1651
|
return $deferred->reject("circular reference detected $val") |
|
|
|
100
|
|
|
|
|
37
|
|
|
|
|
|
|
if $val && Scalar::Util::blessed $val && $val eq $deferred; |
38
|
|
|
|
|
|
|
|
39
|
229
|
100
|
|
|
|
564
|
return $self->resolve_promise($deferred, $val) if is_promise($val); |
40
|
207
|
100
|
|
|
|
916
|
return $self->resolve_thenable($deferred, $val) if is_thenable($val); |
41
|
169
|
|
|
|
|
614
|
$deferred->change_state(FULFILLED, $val); |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
22
|
50
|
|
22
|
1
|
321
|
sub resolve_promise($self, $deferred, $promise) { |
|
22
|
50
|
|
|
|
66
|
|
|
22
|
|
|
|
|
35
|
|
|
22
|
|
|
|
|
26
|
|
|
22
|
|
|
|
|
59
|
|
|
22
|
|
|
|
|
25
|
|
45
|
22
|
|
|
|
|
400
|
my $xd = $promise->deferred; |
46
|
|
|
|
|
|
|
|
47
|
22
|
100
|
|
|
|
189
|
return $deferred->reject("circular reference detected $promise") |
48
|
|
|
|
|
|
|
if $xd == $deferred; |
49
|
19
|
100
|
|
|
|
56
|
return $deferred->change_state($xd->result->@*) if $xd->state != PENDING; |
50
|
|
|
|
|
|
|
|
51
|
6
|
|
|
|
|
138
|
$xd->subscribe($deferred->builder->listener(deferred => $deferred)); |
52
|
6
|
|
|
|
|
133
|
$deferred->is_adaptor(1); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
38
|
50
|
|
38
|
1
|
98
|
sub resolve_thenable($self, $deferred, $thenable) { |
|
38
|
50
|
|
|
|
86
|
|
|
38
|
|
|
|
|
47
|
|
|
38
|
|
|
|
|
41
|
|
|
38
|
|
|
|
|
40
|
|
|
38
|
|
|
|
|
36
|
|
56
|
|
|
|
|
|
|
|
57
|
38
|
|
|
|
|
42
|
my $was_called = 0; |
58
|
38
|
|
|
|
|
100
|
my $registered = $self->thenables($deferred); |
59
|
38
|
|
|
|
|
107
|
$registered->{$thenable}++; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# resolved cb, if called exceptions will be skipped after that |
62
|
30
|
50
|
|
30
|
|
2293
|
my $resolve = sub($v) { |
|
30
|
50
|
|
|
|
74
|
|
|
30
|
|
|
|
|
44
|
|
|
30
|
|
|
|
|
26
|
|
63
|
30
|
100
|
|
|
|
72
|
return if $was_called++; |
64
|
28
|
100
|
|
|
|
134
|
return $deferred->reject("Circular thenable $v") if $registered->{$v}++; |
65
|
23
|
|
|
|
|
68
|
$deferred->resolve($v); |
66
|
38
|
|
|
|
|
140
|
}; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# reject immidiately changes a state |
69
|
11
|
50
|
|
11
|
|
440
|
my $reject = sub($r) { |
|
11
|
50
|
|
|
|
24
|
|
|
11
|
|
|
|
|
13
|
|
|
11
|
|
|
|
|
12
|
|
70
|
11
|
100
|
|
|
|
29
|
return if $was_called++; |
71
|
9
|
|
|
|
|
32
|
$deferred->reject($r); |
72
|
38
|
|
|
|
|
103
|
}; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# resolution 3.III.d |
75
|
38
|
|
|
|
|
41
|
my $err; |
76
|
38
|
|
|
|
|
37
|
CATCH: { |
77
|
38
|
|
|
|
|
38
|
local $@; |
78
|
38
|
|
|
|
|
48
|
eval { $thenable->then($resolve, $reject) }; |
|
38
|
|
|
|
|
111
|
|
79
|
38
|
|
|
|
|
178
|
$err = $@; |
80
|
|
|
|
|
|
|
} |
81
|
38
|
100
|
100
|
|
|
327
|
$deferred->reject($err) if $err && !$was_called; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
1; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
__END__ |