Async GuidesBest Practices

Best Practices

This guide gives an overview of best practices for using Async.

Use a top-level Sync to denote the root of your program

The Sync method ensures your code is running in a reactor or creates one if necessary, and has synchronous semantics, i.e. you do not need to wait on the result of the block.

require 'async'

class Packages
	def initialize(urls)
		@urls = urls
	end
	
	def fetch
		Sync do |task|
			@urls.map do |url|
				task.async do
					fetch(url)
				end
			end.map(&:wait)
		end
	end
end

Use a barrier to wait for all tasks to complete

A barrier ensures you don't leak tasks and that all tasks are completed or stopped before progressing.

require 'async'
require 'async/barrier'

class Packages
	def initialize(urls)
		@urls = urls
	end
	
	def fetch
		barrier = Async::Barrier.new
			
		Sync do
			@urls.map do |url|
				barrier.async do
					fetch(url)
				end
			end.map(&:wait)
		ensure
			barrier.stop
		end
	end
end

Use a semaphore to limit the number of concurrent tasks

Unbounded concurrency is a common source of bugs. Use a semaphore to limit the number of concurrent tasks.

require 'async'
require 'async/barrier'
require 'async/semaphore'

class Packages
	def initialize(urls)
		@urls = urls
	end
	
	def fetch
		barrier = Async::Barrier.new

		Sync do
			# Only 10 tasks are created at a time:
			semaphore = Async::Semaphore.new(10, parent: barrier)
			
			@urls.map do |url|
				semaphore.async do
					fetch(url)
				end
			end.map(&:wait)
		ensure
			barrier.stop
		end
	end
end