Go OR-Done-Channel Pattern: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
No edit summary
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
=Internal=
=Internal=
* [[Go_Channels#Use_a_done_Channel|Go Channels]]
* [[Go_Channels#Use_a_done_Channel|The <tt>done</tt> Channel]]
* [[Go_Channels#Channel_Patterns|Channel Patterns]]


=Overview=
=Overview=
Line 8: Line 10:
   // do something with the channel value, we exit the loop automatically when the channel is closed
   // do something with the channel value, we exit the loop automatically when the channel is closed
   ...
   ...
}
</syntaxhighlight>
The <code>for</code>/<code>range</code> syntax is simple and intuitive, but it cannot be used anymore if we want to [[Go_Channels#Use_a_done_Channel|make the loop preemptable by using a <code>done</code> channel]]. In that case we need to replace it with a <code>for</code>/<code>select</code> syntax and <code>case</code> for the state of the <code>done</code> channel (see an example [[Go_Channels#EB90|here]]).
The equivalent syntax is:
<syntaxhighlight lang='go'>
for v := range orDone(done, c) {
  // do something with the channel value, we exit the loop automatically when the 'c' channel is closed
  // or when the 'done' channel is messaged (written onto or closed)
  ...
}
</syntaxhighlight>
=<tt>orDone()</tt>=
<syntaxhighlight lang='go'>
// orDone iterates over the input channel 'in', sending the incoming elements to the output channel returned
// as the result of the function, until the 'done' channel is messaged (written onto or closed).
func orDone(done <-chan interface{}, in <-chan interface{}) <-chan interface{} {
var out = make(chan interface{})
go func() {
defer close(out)
for {
select {
case <-done:
// the 'done' channel was messaged, exit the closure, which will close the output channel
return
case v, inputChanStillOpen := <-in:
if !inputChanStillOpen {
// input channel closed, exit the closure, which will close the output channel
return
}
out <- v
}
}
}()
return out
}
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 23:06, 5 February 2024

External

Internal

Overview

Reading from a channel until the channel is closed can be done by ranging over a channel:

for v := range c {
  // do something with the channel value, we exit the loop automatically when the channel is closed
  ...
}

The for/range syntax is simple and intuitive, but it cannot be used anymore if we want to make the loop preemptable by using a done channel. In that case we need to replace it with a for/select syntax and case for the state of the done channel (see an example here).

The equivalent syntax is:

for v := range orDone(done, c) {
  // do something with the channel value, we exit the loop automatically when the 'c' channel is closed
  // or when the 'done' channel is messaged (written onto or closed)
  ...
}

orDone()

// orDone iterates over the input channel 'in', sending the incoming elements to the output channel returned
// as the result of the function, until the 'done' channel is messaged (written onto or closed).
func orDone(done <-chan interface{}, in <-chan interface{}) <-chan interface{} {
	var out = make(chan interface{})
	go func() {
		defer close(out)
		for {
			select {
			case <-done:
				// the 'done' channel was messaged, exit the closure, which will close the output channel
				return
			case v, inputChanStillOpen := <-in:
				if !inputChanStillOpen {
					// input channel closed, exit the closure, which will close the output channel
					return
				}
				out <- v
			}
		}
	}()
	return out
}