Closing Channels in Nim

import std/[asyncdispatch, asyncfutures]

proc main() {.async.} =
  var jobs = newAsyncChannel[int](5)
  var done = newAsyncFuture[bool]()

  # Here's the worker procedure. It repeatedly receives
  # from `jobs` with `let (j, more) = await jobs.recv()`. In this
  # special 2-value form of receive, the `more` value
  # will be `false` if `jobs` has been `close`d and all
  # values in the channel have already been received.
  # We use this to notify on `done` when we've worked
  # all our jobs.
  asyncSpawn(async proc() =
    while true:
      let (j, more) = await jobs.recv()
      if more:
        echo "received job ", j
      else:
        echo "received all jobs"
        done.complete(true)
        return
  )

  # This sends 3 jobs to the worker over the `jobs`
  # channel, then closes it.
  for j in 1..3:
    await jobs.send(j)
    echo "sent job ", j
  jobs.close()
  echo "sent all jobs"

  # We await the worker using the synchronization approach
  # we saw earlier.
  discard await done

  # Reading from a closed channel succeeds immediately,
  # returning the zero value of the underlying type.
  # The optional second return value is `true` if the
  # value received was delivered by a successful send
  # operation to the channel, or `false` if it was a
  # zero value generated because the channel is closed
  # and empty.
  let (_, ok) = await jobs.recv()
  echo "received more jobs: ", ok

waitFor main()

In this example, we demonstrate how to use asynchronous channels in Nim to communicate work between different asynchronous procedures. This is similar to using goroutines and channels in other languages.

We use the asyncdispatch and asyncfutures modules to work with asynchronous programming in Nim. The AsyncChannel type is used to create a buffered channel for communication between asynchronous procedures.

The main procedure is marked as async and contains the main logic of our program. We create an AsyncChannel for jobs and an AsyncFuture for synchronization.

We define a worker procedure using asyncSpawn which continuously receives jobs from the channel. It uses pattern matching to check if more jobs are available or if the channel has been closed.

In the main procedure, we send three jobs to the worker, close the channel, and then wait for the worker to complete processing all jobs.

Finally, we demonstrate how to check if a channel is closed by attempting to receive from it after it has been closed.

To run this program, save it as closing_channels.nim and use the following command:

$ nim c -r closing_channels.nim

This will compile and run the Nim program, producing output similar to:

sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false

This example showcases Nim’s approach to concurrent programming using asynchronous procedures and channels, which provides similar functionality to goroutines and channels in other languages.