rosarium 0.1.2 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dd135d7400b2b9b0ec7a42e0f55574e303df8425
4
- data.tar.gz: bebfd30e1de878fbedf6590c32a4b7909b479288
2
+ SHA256:
3
+ metadata.gz: 3789051cf47569f1729bb161433ecb033274b49833e774eec788e5884c490897
4
+ data.tar.gz: f48ef4f93bc8cadb68d9069d3dd3f8434b45ce43e6f3057918912d97d72f5432
5
5
  SHA512:
6
- metadata.gz: 2f4a5068349c9084a3aa7b9207bb36df467658f3492d388f622f4dad39870187e175dc1671effddf4b9fcb839a1167f9b61431a4fc3247322b522c7b6ee12881
7
- data.tar.gz: aa809dc94d73d35b5a2dea871dfeb486b04e1dc5b1733955d5f3cf0dab566d558f5b2ddbded6558d011b54f726379112eb47a7f8edd9f758fc9a3a641727bd4f
6
+ metadata.gz: 3e108887ddb2cdee8bc0e5abe53915e249028d5f367a7c617874460a4b7b5ec19ca04c364631e3b6235c0da80c2ae2951501e08c6b280dd2609455bdbe9f7858
7
+ data.tar.gz: 7622eab1b92fcee040b91394422f8b7f1f2c78be004c62988d5e588bf528ab6718da0bc97d3adb8b24022b22498d80843e31c051b5b83ed2f4fed308ff288370
data/Gemfile CHANGED
@@ -1 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem 'rspec', '~> 3.4'
4
+ gem 'rubocop', '~> 1.2'
data/Gemfile.lock CHANGED
@@ -1,25 +1,46 @@
1
1
  GEM
2
2
  specs:
3
- diff-lcs (1.2.5)
4
- rspec (3.4.0)
5
- rspec-core (~> 3.4.0)
6
- rspec-expectations (~> 3.4.0)
7
- rspec-mocks (~> 3.4.0)
8
- rspec-core (3.4.1)
9
- rspec-support (~> 3.4.0)
10
- rspec-expectations (3.4.0)
3
+ ast (2.4.1)
4
+ diff-lcs (1.4.4)
5
+ parallel (1.20.1)
6
+ parser (3.0.0.0)
7
+ ast (~> 2.4.1)
8
+ rainbow (3.0.0)
9
+ regexp_parser (1.8.2)
10
+ rexml (3.2.4)
11
+ rspec (3.9.0)
12
+ rspec-core (~> 3.9.0)
13
+ rspec-expectations (~> 3.9.0)
14
+ rspec-mocks (~> 3.9.0)
15
+ rspec-core (3.9.2)
16
+ rspec-support (~> 3.9.3)
17
+ rspec-expectations (3.9.2)
11
18
  diff-lcs (>= 1.2.0, < 2.0)
12
- rspec-support (~> 3.4.0)
13
- rspec-mocks (3.4.0)
19
+ rspec-support (~> 3.9.0)
20
+ rspec-mocks (3.9.1)
14
21
  diff-lcs (>= 1.2.0, < 2.0)
15
- rspec-support (~> 3.4.0)
16
- rspec-support (3.4.1)
22
+ rspec-support (~> 3.9.0)
23
+ rspec-support (3.9.3)
24
+ rubocop (1.2.0)
25
+ parallel (~> 1.10)
26
+ parser (>= 2.7.1.5)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8)
29
+ rexml
30
+ rubocop-ast (>= 1.0.1)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 1.4.0, < 2.0)
33
+ rubocop-ast (1.4.0)
34
+ parser (>= 2.7.1.5)
35
+ ruby-progressbar (1.11.0)
36
+ unicode-display_width (1.7.0)
17
37
 
18
38
  PLATFORMS
19
39
  ruby
20
40
 
21
41
  DEPENDENCIES
22
42
  rspec (~> 3.4)
43
+ rubocop (~> 1.2)
23
44
 
24
45
  BUNDLED WITH
25
- 1.11.2
46
+ 1.17.3
data/README.md CHANGED
@@ -66,9 +66,15 @@ then later, use the "deferred" to fulfill or reject the promise:
66
66
  ## Methods of promises:
67
67
 
68
68
  ```
69
- # One of: :pending, :resolving, :fulfilled, :rejected.
69
+ # One of: :pending, :fulfilled, :rejected.
70
70
  promise.state
71
71
 
72
+ # Equivalent to "state == :fulfilled"
73
+ promise.fulfilled?
74
+
75
+ # Equivalent to "state == :rejected"
76
+ promise.rejected?
77
+
72
78
  # Wait for the promise to be settled, then return its value (if fulfilled -
73
79
  # note the value may be nil), or nil (if rejected).
74
80
  promise.value
@@ -77,22 +83,13 @@ then later, use the "deferred" to fulfill or reject the promise:
77
83
  # or nil (if fulfilled).
78
84
  promise.reason
79
85
 
80
- # A hash describing the state of the promise. Always includes :state key;
81
- # may include :value or :reason.
82
- promise.inspect
83
-
84
- # true iff state == :fulfilled
85
- promise.fulfilled?
86
-
87
- # true iff state == :rejected
88
- promise.rejected?
89
-
90
86
  # Wait for the promise to be settled, then return its value (if fulfilled),
91
87
  # or raise with the rejection reason (if rejected).
92
88
  promise.value!
93
89
 
94
- # Wait for the promise to be settled
95
- promise.wait
90
+ # A hash describing the state of the promise. Always includes :state key;
91
+ # may include :value or :reason.
92
+ promise.inspect
96
93
  ```
97
94
 
98
95
  Chaining promises together:
data/lib/rosarium.rb CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'rosarium/fixed_thread_executor'
2
- require_relative 'rosarium/simple_promise'
3
4
  require_relative 'rosarium/deferred'
4
5
  require_relative 'rosarium/promise'
5
6
 
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class Deferred
4
5
 
5
6
  def initialize(promise, resolver, rejecter)
@@ -8,9 +9,7 @@ module Rosarium
8
9
  @rejecter = rejecter
9
10
  end
10
11
 
11
- def promise
12
- @promise
13
- end
12
+ attr_reader :promise
14
13
 
15
14
  def resolve(value)
16
15
  @resolver.call(value)
@@ -21,5 +20,4 @@ module Rosarium
21
20
  end
22
21
 
23
22
  end
24
-
25
23
  end
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class FixedThreadExecutor
4
5
 
5
6
  def initialize(max = 1)
@@ -14,7 +15,7 @@ module Rosarium
14
15
  @mutex.synchronize do
15
16
  @waiting << block
16
17
  if @executing < @max
17
- @executing = @executing + 1
18
+ @executing += 1
18
19
  t = Thread.new { execute_and_count_down }
19
20
  @threads.push t
20
21
  end
@@ -36,26 +37,23 @@ module Rosarium
36
37
  private
37
38
 
38
39
  def execute_and_count_down
39
- begin
40
- execute
41
- ensure
42
- @mutex.synchronize do
43
- @executing = @executing - 1
44
- end
40
+ execute
41
+ ensure
42
+ @mutex.synchronize do
43
+ @executing -= 1
45
44
  end
46
45
  end
47
46
 
48
47
  def execute
49
- while true
48
+ loop do
50
49
  block = @mutex.synchronize { @waiting.shift }
51
50
  block or break
52
51
  begin
53
52
  block.call
54
- rescue Exception => e
53
+ rescue Exception
55
54
  end
56
55
  end
57
56
  end
58
57
 
59
58
  end
60
-
61
59
  end
@@ -1,15 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rosarium
4
+ class Promise
2
5
 
3
- class Promise < SimplePromise
6
+ private_class_method :new
4
7
 
5
- DEFAULT_ON_FULFILL = Proc.new {|value| value}
6
- DEFAULT_ON_REJECT = Proc.new {|reason| raise reason}
8
+ def self.defer
9
+ promise = new
7
10
 
8
- def self.resolve(value)
9
- if value.kind_of? Promise
10
- return value
11
+ resolver = lambda do |value|
12
+ promise.send(:try_settle, value, nil)
13
+ nil # do not leak
14
+ end
15
+
16
+ rejecter = lambda do |reason|
17
+ raise "reason must be an Exception" unless reason.is_a?(Exception)
18
+ promise.send(:try_settle, nil, reason)
19
+ nil # do not leak
11
20
  end
12
21
 
22
+ Deferred.new(promise, resolver, rejecter)
23
+ end
24
+
25
+ def self.resolve(value)
26
+ return value if value.is_a? Promise
27
+
13
28
  deferred = defer
14
29
  deferred.resolve(value)
15
30
  deferred.promise
@@ -22,15 +37,7 @@ module Rosarium
22
37
  end
23
38
 
24
39
  def self.execute(&block)
25
- deferred = defer
26
- EXECUTOR.submit do
27
- begin
28
- deferred.resolve block.call
29
- rescue Exception => e
30
- deferred.reject e
31
- end
32
- end
33
- deferred.promise
40
+ @resolved.then(&block)
34
41
  end
35
42
 
36
43
  def self.all_settled(promises)
@@ -42,7 +49,7 @@ module Rosarium
42
49
  waiting_for = promises.count
43
50
  mutex = Mutex.new
44
51
 
45
- check = Proc.new do
52
+ check = proc do
46
53
  # Includes both fulfilled and rejected, so always hits zero eventually
47
54
  if mutex.synchronize { (waiting_for -= 1) == 0 }
48
55
  deferred.resolve promises
@@ -50,7 +57,7 @@ module Rosarium
50
57
  end
51
58
 
52
59
  promises.each do |promise|
53
- promise.then(check) { check.call }
60
+ promise.send(:when_settled, &check)
54
61
  end
55
62
 
56
63
  deferred.promise
@@ -65,38 +72,89 @@ module Rosarium
65
72
  waiting_for = promises.count
66
73
  mutex = Mutex.new
67
74
 
68
- do_reject = Proc.new {|reason| deferred.reject reason}
69
- do_fulfill = Proc.new do
70
- # Only fulfilled (not rejected), so hits zero iff all promises were fulfilled
71
- if mutex.synchronize { (waiting_for -= 1) == 0 }
72
- deferred.resolve(promises.map &:value)
75
+ check = lambda do |promise|
76
+ if promise.fulfilled?
77
+ # Hits zero iff all promises were fulfilled
78
+ if mutex.synchronize { (waiting_for -= 1) == 0 }
79
+ deferred.resolve(promises.map(&:value))
80
+ end
81
+ else
82
+ deferred.reject(promise.reason)
73
83
  end
74
84
  end
75
85
 
76
86
  promises.each do |promise|
77
- promise.then(do_reject) { do_fulfill.call }
87
+ promise.send(:when_settled) { check.call(promise) }
78
88
  end
79
89
 
80
90
  deferred.promise
81
91
  end
82
92
 
93
+ def initialize
94
+ @state = :pending
95
+ @settling = false
96
+ @mutex = Mutex.new
97
+ @condition = ConditionVariable.new
98
+ @when_settled = []
99
+ end
100
+
101
+ def state
102
+ synchronize { @state }
103
+ end
104
+
105
+ def value
106
+ wait_until_settled
107
+ synchronize { @value }
108
+ end
109
+
110
+ def reason
111
+ wait_until_settled
112
+ synchronize { @reason }
113
+ end
114
+
115
+ def value!
116
+ wait_until_settled
117
+ synchronize do
118
+ raise @reason if @state == :rejected
119
+ @value
120
+ end
121
+ end
122
+
123
+ def inspect
124
+ synchronize do
125
+ r = { state: @state }
126
+ r[:value] = @value if @state == :fulfilled
127
+ r[:reason] = @reason if @state == :rejected
128
+ r
129
+ end
130
+ end
131
+
132
+ def fulfilled?
133
+ state == :fulfilled
134
+ end
135
+
136
+ def rejected?
137
+ state == :rejected
138
+ end
139
+
83
140
  def then(on_rejected = nil, &on_fulfilled)
84
141
  deferred = self.class.defer
85
142
 
86
- on_fulfilled ||= DEFAULT_ON_FULFILL
87
- on_rejected ||= DEFAULT_ON_REJECT
88
-
89
- on_resolution do
90
- begin
91
- deferred.resolve(
92
- if fulfilled?
93
- on_fulfilled.call value
94
- else
95
- on_rejected.call reason
96
- end
97
- )
98
- rescue Exception => e
99
- deferred.reject e
143
+ when_settled do
144
+ EXECUTOR.submit do
145
+ begin
146
+ deferred.resolve(
147
+ if fulfilled?
148
+ # User-supplied code
149
+ on_fulfilled ? on_fulfilled.call(value) : value
150
+ else
151
+ # User-supplied code
152
+ on_rejected ? on_rejected.call(reason) : raise(reason)
153
+ end
154
+ )
155
+ rescue Exception => e
156
+ deferred.reject e
157
+ end
100
158
  end
101
159
  end
102
160
 
@@ -107,9 +165,76 @@ module Rosarium
107
165
  self.then(block)
108
166
  end
109
167
 
110
- alias_method :catch, :rescue
111
- alias_method :on_error, :rescue
168
+ alias catch rescue
169
+ alias on_error rescue
112
170
 
113
- end
171
+ private
172
+
173
+ def wait_until_settled
174
+ synchronize do
175
+ loop do
176
+ return if @state != :pending
177
+ @condition.wait @mutex
178
+ end
179
+ end
180
+ end
181
+
182
+ def synchronize(&block)
183
+ @mutex.synchronize(&block)
184
+ end
185
+
186
+ # Can be called more than once
187
+ def try_settle(value, reason)
188
+ settle_with = nil
189
+
190
+ synchronize do
191
+ return if @state != :pending || @settling
192
+
193
+ if value.is_a? Promise
194
+ @settling = true
195
+ elsif reason.nil?
196
+ settle_with = [value, nil]
197
+ else
198
+ settle_with = [nil, reason]
199
+ end
200
+ end
201
+
202
+ if settle_with
203
+ settle(*settle_with)
204
+ else
205
+ value.when_settled do
206
+ if value.fulfilled?
207
+ settle(value.value, nil)
208
+ else
209
+ settle(nil, value.reason)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ # Only called once
216
+ def settle(value, reason)
217
+ synchronize do
218
+ @state = (reason ? :rejected : :fulfilled)
219
+ @value = value
220
+ @reason = reason
221
+ @condition.broadcast
222
+ @when_settled.slice!(0, @when_settled.length)
223
+ end.each(&:call)
224
+ end
114
225
 
226
+ protected
227
+
228
+ def when_settled(&block)
229
+ immediate = synchronize do
230
+ @when_settled << block if @state == :pending
231
+ @state != :pending
232
+ end
233
+
234
+ block.call if immediate
235
+ end
236
+
237
+ @resolved = resolve(nil)
238
+
239
+ end
115
240
  end
@@ -63,7 +63,7 @@ describe "deferred promises" do
63
63
  d1.resolve(d2.promise)
64
64
  check_pending d1.promise
65
65
  d2.resolve 7
66
- d1.promise.wait
66
+ d1.promise.value
67
67
  check_fulfilled d1.promise, 7
68
68
  end
69
69
 
@@ -74,7 +74,7 @@ describe "deferred promises" do
74
74
  check_pending d1.promise
75
75
  e = an_error
76
76
  d2.reject e
77
- d1.promise.wait
77
+ d1.promise.value
78
78
  check_rejected d1.promise, e
79
79
  end
80
80
 
@@ -33,7 +33,7 @@ describe Rosarium::Promise do
33
33
  chained = deferred.promise.then { then_called = true }
34
34
  check_pending chained
35
35
  deferred.reject e
36
- chained.wait
36
+ chained.value
37
37
  check_rejected chained, e
38
38
  expect(then_called).to be_falsy
39
39
  end
@@ -45,7 +45,7 @@ describe Rosarium::Promise do
45
45
  got_args = nil
46
46
  chained = deferred.promise.then(Proc.new {|*args| got_args = args; raise e2 }) { raise "should never be called" }
47
47
  deferred.reject e
48
- chained.wait
48
+ chained.value
49
49
  check_rejected chained, e2
50
50
  expect(got_args).to eq([e])
51
51
  end
@@ -54,7 +54,7 @@ describe Rosarium::Promise do
54
54
  deferred = Rosarium::Promise.defer
55
55
  chained = deferred.promise.then(Proc.new {7}) { raise "should never be called" }
56
56
  deferred.reject an_error
57
- chained.wait
57
+ chained.value
58
58
  check_fulfilled chained, 7
59
59
  end
60
60
 
@@ -63,9 +63,16 @@ describe Rosarium::Promise do
63
63
  deferred = Rosarium::Promise.defer
64
64
  chained = deferred.promise.send(method) { 7 }
65
65
  deferred.reject an_error
66
- chained.wait
66
+ chained.value
67
67
  check_fulfilled chained, 7
68
68
  end
69
69
  end
70
70
 
71
+ it "uses the executor even if immediately callable" do
72
+ deferred = Rosarium::Promise.resolve(nil).then { sleep 0.1; 7 }
73
+ check_pending deferred
74
+ sleep 0.2
75
+ check_fulfilled deferred, 7
76
+ end
77
+
71
78
  end
@@ -14,16 +14,11 @@ describe "instantly-resolved promises" do
14
14
  it "creates a fulfilled promise" do
15
15
  t = Rosarium::Promise.resolve 7
16
16
  check_fulfilled t, 7
17
- expect(t).not_to respond_to(:fulfill)
18
- expect(t).not_to respond_to(:reject)
19
17
  end
20
18
 
21
19
  it "creates a rejected promise" do
22
- e = an_error
23
20
  t = Rosarium::Promise.reject an_error
24
21
  check_rejected t, an_error
25
- expect(t).not_to respond_to(:fulfill)
26
- expect(t).not_to respond_to(:reject)
27
22
  end
28
23
 
29
24
  it "creates an immediately-executable promise" do
@@ -61,7 +56,7 @@ describe "instantly-resolved promises" do
61
56
 
62
57
  e = an_error
63
58
  d2.reject e
64
- promise.wait
59
+ promise.value
65
60
  check_fulfilled promise, [ d1.promise, d2.promise ]
66
61
  end
67
62
 
@@ -82,7 +77,7 @@ describe "instantly-resolved promises" do
82
77
 
83
78
  e = an_error
84
79
  d3.reject e
85
- promise.wait
80
+ promise.value
86
81
  check_rejected promise, e
87
82
  end
88
83
 
@@ -96,7 +91,7 @@ describe "instantly-resolved promises" do
96
91
  check_pending promise
97
92
 
98
93
  d2.resolve 8
99
- promise.wait
94
+ promise.value
100
95
  check_fulfilled promise, [7,8]
101
96
  end
102
97
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rosarium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rachel Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-04 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
27
41
  description: |2
28
42
  Rosarium implements something that's a bit like Promises,
29
43
  inspired by the stability and ease of use of Q
@@ -41,7 +55,6 @@ files:
41
55
  - lib/rosarium/deferred.rb
42
56
  - lib/rosarium/fixed_thread_executor.rb
43
57
  - lib/rosarium/promise.rb
44
- - lib/rosarium/simple_promise.rb
45
58
  - spec/deferred_spec.rb
46
59
  - spec/fixed_thread_executor_spec.rb
47
60
  - spec/promise_methods_spec.rb
@@ -59,15 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
72
  requirements:
60
73
  - - ">="
61
74
  - !ruby/object:Gem::Version
62
- version: '0'
75
+ version: '2.4'
63
76
  required_rubygems_version: !ruby/object:Gem::Requirement
64
77
  requirements:
65
78
  - - ">="
66
79
  - !ruby/object:Gem::Version
67
80
  version: '0'
68
81
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.5.1
82
+ rubygems_version: 3.0.3
71
83
  signing_key:
72
84
  specification_version: 4
73
85
  summary: Promises, or something like them
@@ -1,164 +0,0 @@
1
- module Rosarium
2
-
3
- class SimplePromise
4
-
5
- def self.defer
6
- promise = new
7
- resolver = promise.method :resolve
8
- rejecter = promise.method :reject
9
-
10
- class <<promise
11
- undef :resolve
12
- undef :reject
13
- end
14
-
15
- Deferred.new(promise, resolver, rejecter)
16
- end
17
-
18
- def initialize
19
- @state = :pending
20
- @resolving = false
21
- @mutex = Mutex.new
22
- @condition = ConditionVariable.new
23
- @on_resolution = []
24
- end
25
-
26
- def state
27
- synchronize { @state }
28
- end
29
-
30
- def value
31
- wait
32
- synchronize { @value }
33
- end
34
-
35
- def reason
36
- wait
37
- synchronize { @reason }
38
- end
39
-
40
- def inspect
41
- synchronize do
42
- r = { state: @state }
43
- r[:value] = @value if @state == :fulfilled
44
- r[:reason] = @reason if @state == :rejected
45
- r
46
- end
47
- end
48
-
49
- def fulfilled?
50
- state == :fulfilled
51
- end
52
-
53
- def rejected?
54
- state == :rejected
55
- end
56
-
57
- def value!
58
- wait
59
- synchronize do
60
- if @state == :rejected
61
- raise @reason
62
- else
63
- @value
64
- end
65
- end
66
- end
67
-
68
- def wait
69
- on_resolution do
70
- @mutex.synchronize { @condition.broadcast }
71
- end
72
-
73
- @mutex.synchronize do
74
- loop do
75
- return if @state == :fulfilled or @state == :rejected
76
- @condition.wait @mutex
77
- end
78
- end
79
- end
80
-
81
- private
82
-
83
- def synchronize
84
- @mutex.synchronize { yield }
85
- end
86
-
87
- public
88
-
89
- def resolve(value)
90
- _resolve(value, nil)
91
- end
92
-
93
- def reject(reason)
94
- raise "reason must be an Exception" unless reason.kind_of? Exception
95
- _resolve(nil, reason)
96
- end
97
-
98
- private
99
-
100
- def _resolve(value, reason)
101
- callbacks = []
102
- add_on_resolution = false
103
-
104
- synchronize do
105
- if @state == :pending and not @resolving
106
- if value.kind_of? SimplePromise
107
- @resolving = true
108
- add_on_resolution = true
109
- elsif reason.nil?
110
- @value = value
111
- @state = :fulfilled
112
- callbacks.concat @on_resolution
113
- @on_resolution.clear
114
- else
115
- @reason = reason
116
- @state = :rejected
117
- callbacks.concat @on_resolution
118
- @on_resolution.clear
119
- end
120
- end
121
- end
122
-
123
- if add_on_resolution
124
- value.on_resolution { copy_resolution_from value }
125
- end
126
-
127
- callbacks.each {|c| EXECUTOR.submit { c.call } }
128
- end
129
-
130
- def copy_resolution_from(other)
131
- callbacks = []
132
-
133
- synchronize do
134
- @value = other.value
135
- @reason = other.reason
136
- @state = other.state
137
- @resolving = false
138
- callbacks.concat @on_resolution
139
- @on_resolution.clear
140
- end
141
-
142
- callbacks.each {|c| EXECUTOR.submit { c.call } }
143
- end
144
-
145
- protected
146
-
147
- def on_resolution(&block)
148
- immediate = synchronize do
149
- if @state == :fulfilled or @state == :rejected
150
- true
151
- else
152
- @on_resolution << block
153
- false
154
- end
155
- end
156
-
157
- block.call if immediate
158
-
159
- nil
160
- end
161
-
162
- end
163
-
164
- end