# How to wait a message on multiple channels?

**URL:** https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421
**Category:** Help & Support
**Created:** [November 20, 2024, 6:54pm UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421 "2024-11-20T18:54:31Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![amigrave](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/amigrave/32/791_2.png) [@amigrave](https://forum.crystal-lang.org/u/amigrave)
#### Post date: [November 20, 2024, 6:54pm UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/1 "2024-11-20T18:54:31Z")

</div>

Hi all.

How can I have an Array of Channels with which I want the current Fiber to wait/listen for a message on all those channels at once and exit the flow either if one of the channel received a message or if a timeout (previously defined) occurred  
?

---

<div class="post-metadata">

### Author: ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)
#### Post date: [November 20, 2024, 6:58pm UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/2 "2024-11-20T18:58:48Z")

</div>

I’d look into [select - Crystal](https://crystal-lang.org/reference/1.14/syntax_and_semantics/select.html).

---

<div class="post-metadata">

### Author: ![amigrave](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/amigrave/32/791_2.png) [@amigrave](https://forum.crystal-lang.org/u/amigrave)
#### Post date: [November 21, 2024, 6:39am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/3 "2024-11-21T06:39:54Z")

</div>

Thanks for your answer, however select seems only appropriate when working with a defined amount of channels, in my case I have a reference to an array of channels which is unknown at compile time.

---

<div class="post-metadata">

### Author: ![sol.vin](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/sol.vin/32/73_2.png) [@sol.vin](https://forum.crystal-lang.org/u/sol.vin)
#### Post date: [November 21, 2024, 8:53am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/4 "2024-11-21T08:53:48Z")

</div>

When working with an array of channels, you may need to spin your own solution to whatever behavior you want to achieve. For example: You may just want to get the messages waiting on the channels, if you care about the order you might do something like:

```crystal
array_of_channels.map(&.receive?)

```

but if you care about a timeout you may need to do something like

```crystal
array_of_channels.each do |channel|
  select
  when item = channel.receive?
    #process item
  when timeout(5.seconds)
    #Handle timeout
  end
end

```

However, this might still have a behavior you don’t want, it waits up to 5 seconds for each channel. Therefore you may need to do something like:

```crystal
wg = WaitGroup.new(array_of_channels.size)
array_of_channels.each do |channel|
  spawn do
    begin
      select
      when item = channel.receive?
        #process item
      when timeout(5.seconds)
        #Handle timeout
      end
    end
  ensure
    wg.done
  end
end

wg.wait #Waits for all the channels to finish.

```

This paradigm only works if you can process the data received from the channel independently, not if you need to process the data all at once. There are a lot of ways to skin a cat on this one.

Let us know more about what you are doing and we can recommend some potential ways to make your code work.

---

<div class="post-metadata">

### Author: ![amigrave](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/amigrave/32/791_2.png) [@amigrave](https://forum.crystal-lang.org/u/amigrave)
#### Post date: [November 22, 2024, 12:05am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/5 "2024-11-22T00:05:58Z")

</div>

Thanks for your response.  
I think none of those solutions will match my use case.

I want to wait for any of the channels to receive a message (I’m using an Array of `Channel(Nil)`) , hence I want the waiting to be interrupted either as soon as one Channel receives a `Nil` , either if a timeout is reached while waiting.

Is there a solution for that ?

---

<div class="post-metadata">

### Author: ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)
#### Post date: [November 22, 2024, 12:06am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/6 "2024-11-22T00:06:23Z")

</div>

Does your use case preclude putting all these items into one channel?

---

<div class="post-metadata">

### Author: ![bcardiff](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bcardiff/32/3_2.png) [@bcardiff](https://forum.crystal-lang.org/u/bcardiff)
#### Post date: [November 22, 2024, 1:13am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/7 "2024-11-22T01:13:39Z")

</div>

I think you want to use the `:nodoc:` method `Channel.non_blocking_select`.

See usages in [crystal/spec/std/channel\_spec.cr at master · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/blob/master/spec/std/channel_spec.cr#L577)

And in [crystal/spec/std/channel\_spec.cr at master · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/blob/master/spec/std/channel_spec.cr#L557) you can see how to create a `timeout_select_action`.

Note, is an internal api as we didn’t envision arbitrary amount of channels to be a common idiom when designing the public api.

---

<div class="post-metadata">

### Author: ![amigrave](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/amigrave/32/791_2.png) [@amigrave](https://forum.crystal-lang.org/u/amigrave)
#### Post date: [November 22, 2024, 10:45am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/8 "2024-11-22T10:45:06Z")

</div>

> Does your use case preclude putting all these items into one channel?

@jgaskins Technically no, but design-wise I think so. If I do that I will end up with 5000 to 10000 Fibers receiving a message at the same time, all checking if this message is for them, while only a very few of them are concerned.  
I’m afraid that this design could induce a cpu bound solution which I’m actually trying to avoid, considering that I’m currently evaluating Crystal for the first time in a production context to replace a Python gevented solution that becomes a bottleneck.

> I think you want to use the `:nodoc:` method `Channel.non_blocking_select` .

Thanks for your response @bcardiff . I will have a look to this. Does it imply to implicitly or explicitly spawn one Fiber per channel to receive from ?

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [November 22, 2024, 11:05am UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/9 "2024-11-22T11:05:36Z")

</div>

Maybe you could tell us a bit more about what you’re actually trying to do?

From the initial description I gathered you’d want one fiber to wait for a signal from either of a number of other fibers. But it seems there is more going on. Hearing about the full context would allow to help you better find a solution for your problem.

---

<div class="post-metadata">

### Author: ![bcardiff](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bcardiff/32/3_2.png) [@bcardiff](https://forum.crystal-lang.org/u/bcardiff)
#### Post date: [November 22, 2024, 12:35pm UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/10 "2024-11-22T12:35:58Z")

</div>

> [@amigrave](#):
>
> Does it imply to implicitly or explicitly spawn one Fiber per channel to receive from ?

No.

`non_blocking_select` is what is used by the `select` statement. The compiler generates a call to the former from the later. Syntax wise there is no way to have a dynamic number of `when`s, but the `non_blocking_select` works on `Indexable` (so `Array` can be used)

---

<div class="post-metadata">

### Author: ![amigrave](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/amigrave/32/791_2.png) [@amigrave](https://forum.crystal-lang.org/u/amigrave)
#### Post date: [November 22, 2024, 6:27pm UTC](https://forum.crystal-lang.org/t/how-to-wait-a-message-on-multiple-channels/7421/11 "2024-11-22T18:27:41Z")

</div>

Well I tried to be as less verbose as possible to prevent loosing people’s time but it seems it’s too late for that.

That being said I realised that I might have another option that I should try before going further in the direction I was headed to. I’ll come back to tell if it was paying of and I might end-up competing two models to see which one is better.
