Async

Async is a gem, not a language feature.

bundle add async

Async and wait

The top level Async block is needed to create a reactor context to do the scheduing, otherwise the first puts won't get executed utill the task is finished.

require 'async'

Async do
  task = Async do
    sleep 1
    rand
  end
  
  puts "wait for 1 second"
  puts task.wait
end

Concurrency

Async do
  tasks = (1..5).map do |x|
    Async do
      sleep 1
      x
    end
  end
  puts tasks.map(&:wait)
end

Limiting Concurrency Level

require 'async'
require 'async/semaphore'

Async do
  semaphore = Async::Semaphore.new(2)

  (1..5).each do |x|
    semaphore.async do
      puts "#{x} started"
      sleep 1
      puts "         #{x} finished"
    end
  end
end

Waiting for tasks

Barrier can be used to wait for a group of tasks, but you can just simplely do tasks.each(&:wait).

Waiter can be used to wait for a specific number of tasks from all tasks, kind of like Promise.race in js or select in go, but more flexible.

Barrier and Waiter can be used together.

require 'async'
require 'async/waiter'
require 'async/barrier'

barrier = Async::Barrier.new
waiter = Async::Waiter.new(parent: barrier)

Async do
  [3, 2, 1].each do |x|
    waiter.async do
      sleep x
      x
    end
  end
	
  puts waiter.wait(2).inspect
	
  # stop the remaining jobs
  barrier.stop
end

this should puts

[1, 2]

Timeouts

require 'async'

Async do |task|
  task.with_timeout(0.5) do
    sleep rand
  rescue Async::TimeoutError
    puts "timeout!"
  end
end