Class: Cql::Future

Inherits:
Object
  • Object
show all
Defined in:
lib/cql/future.rb

Overview

A future represents the value of a process that may not yet have completed.

Direct Known Subclasses

CombinedFuture, CompletedFuture, FailedFuture

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFuture

Returns a new instance of Future.



12
13
14
15
16
17
# File 'lib/cql/future.rb', line 12

def initialize
  @complete_listeners = []
  @failure_listeners = []
  @value_barrier = Queue.new
  @state_lock = Mutex.new
end

Class Method Details

.combine(*futures) ⇒ Future<Array>

Combine multiple futures into a new future which completes when all constituent futures complete, or fail when one or more of them fails.

The value of the combined future is an array of the values of the constituent futures.

Parameters:

  • futures (Array<Future>)

    the futures to combine

Returns:

  • (Future<Array>)

    an array of the values of the constituent futures



28
29
30
31
32
33
34
# File 'lib/cql/future.rb', line 28

def self.combine(*futures)
  if futures.any?
    CombinedFuture.new(*futures)
  else
    completed([])
  end
end

.completed(value = nil) ⇒ Future

Creates a new future which is completed.

Parameters:

  • value (Object, nil) (defaults to: nil)

    the value of the created future

Returns:

  • (Future)

    a completed future



41
42
43
# File 'lib/cql/future.rb', line 41

def self.completed(value=nil)
  CompletedFuture.new(value)
end

.failed(error) ⇒ Future

Creates a new future which is failed.

Parameters:

  • error (Error)

    the error of the created future

Returns:

  • (Future)

    a failed future



50
51
52
# File 'lib/cql/future.rb', line 50

def self.failed(error)
  FailedFuture.new(error)
end

Instance Method Details

#complete!(v = nil) ⇒ Object

Completes the future.

This will trigger all completion listeners in the calling thread.

Parameters:

  • v (Object) (defaults to: nil)

    the value of the future



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/cql/future.rb', line 60

def complete!(v=nil)
  @state_lock.synchronize do
    raise FutureError, 'Future already completed' if complete? || failed?
    @value = v
    @complete_listeners.each do |listener|
      listener.call(@value)
    end
  end
ensure
  @state_lock.synchronize do
    @value_barrier << :ping
  end
end

#complete?Boolean

Returns whether or not the future is complete

Returns:

  • (Boolean)


76
77
78
# File 'lib/cql/future.rb', line 76

def complete?
  defined? @value
end

#fail!(error) ⇒ Object

Fails the future.

This will trigger all failure listeners in the calling thread.

Parameters:

  • error (Error)

    the error which prevented the value from being determined



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/cql/future.rb', line 115

def fail!(error)
  @state_lock.synchronize do
    raise FutureError, 'Future already completed' if failed? || complete?
    @error = error
    @failure_listeners.each do |listener|
      listener.call(error)
    end
  end
ensure
  @state_lock.synchronize do
    @value_barrier << :ping
  end
end

#failed?Boolean

Returns whether or not the future is failed.

Returns:

  • (Boolean)


131
132
133
# File 'lib/cql/future.rb', line 131

def failed?
  !!@error
end

#flat_map {|value| ... } ⇒ Future

Returns a new future representing a transformation of this future’s value, but where the transformation itself may be asynchronous.

This method is useful when you want to chain asynchronous operations.

Examples:

future2 = future1.flat_map { |value| method_returning_a_future(value) }

Yield Parameters:

  • value (Object)

    the value of this future

Yield Returns:

  • (Future)

    a future representing the transformed value

Returns:

  • (Future)

    a new future representing the transformed value



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/cql/future.rb', line 184

def flat_map(&block)
  fp = Future.new
  on_failure { |e| fp.fail!(e) }
  on_complete do |v|
    begin
      fpp = block.call(v)
      fpp.on_failure { |e| fp.fail!(e) }
      fpp.on_complete do |vv|
        fp.complete!(vv)
      end
    rescue => e
      fp.fail!(e)
    end
  end
  fp
end

#map {|value| ... } ⇒ Future

Returns a new future representing a transformation of this future’s value.

Examples:

future2 = future1.map { |value| value * 2 }

Yield Parameters:

  • value (Object)

    the value of this future

Yield Returns:

  • (Object)

    the transformed value

Returns:

  • (Future)

    a new future representing the transformed value



158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/cql/future.rb', line 158

def map(&block)
  fp = Future.new
  on_failure { |e| fp.fail!(e) }
  on_complete do |v|
    begin
      vv = block.call(v)
      fp.complete!(vv)
    rescue => e
      fp.fail!(e)
    end
  end
  fp
end

#on_complete {|value| ... } ⇒ Object

Registers a listener for when this future completes

Yield Parameters:

  • value (Object)

    the value of the completed future



84
85
86
87
88
89
90
91
92
# File 'lib/cql/future.rb', line 84

def on_complete(&listener)
  @state_lock.synchronize do
    if complete?
      listener.call(value)
    else
      @complete_listeners << listener
    end
  end
end

#on_failure {|error| ... } ⇒ Object

Registers a listener for when this future fails

Yield Parameters:

  • error (Error)

    the error that failed the future



139
140
141
142
143
144
145
146
147
# File 'lib/cql/future.rb', line 139

def on_failure(&listener)
  @state_lock.synchronize do
    if failed?
      listener.call(@error)
    else
      @failure_listeners << listener
    end
  end
end

#valueObject Also known as: get

Returns the value of this future, blocking until it is available, if necessary.

If the future fails this method will raise the error that failed the future.

Returns:

  • (Object)

    the value of this future

Raises:

  • (@error)


100
101
102
103
104
105
106
# File 'lib/cql/future.rb', line 100

def value
  raise @error if @error
  return @value if defined? @value
  @value_barrier.pop
  raise @error if @error
  return @value
end