Ruby 3 has Fiber Scheduler hooks that enable asynchronous programming. In order to make this work you need a Fiber Scheduler implementation, but Ruby does not provide a default one.
This gem aims to fill that void by providing a Fiber Scheduler class that makes a great default. It's easy to use, performant, and can be used with just built-in Ruby methods.
fiber_scheduler's killer feature 💣 is full compatibility with any other
Fiber Scheduler implementation, including the
async gem. Write code using
fiber_scheduler and it works seamlessly with async, bsync or whatever
other _sync gem comes in the future.
Learn more about the Ruby's Fiber Scheduler feature.
gem install fiber_scheduler
Requires Ruby 3.1.
- Enables asynchronous (colorless) programming in Ruby.
 - Killer feature: full compatibility with any other Fiber Scheduler implementation, including the async gem.
 - Not a framework: no DSL or new APIs. Can be used with just built-in Ruby methods: Fiber.set_scheduler and Fiber.schedule.
 - ~500 LOC of pure Ruby, no C extensions.
 - No dependencies.
 
- With a block (recommended)
 - Set 
Fiber.schedulerdirectly 
With a block (recommended)
FiberScheduler do
  # Your code here, e.g. Fiber.schedule { ... }
endRecommended because:
- This approach has full compatibility with any other Fiber Scheduler.
 Fiber.scheduleris automatically un-set outside the block.
Set Fiber.scheduler directly
Fiber.set_scheduler(FiberScheduler.new)
# Your code here, e.g. Fiber.schedule { ... }Fiber.scheduler is set until the end of the current thread, unless manually
unset with Fiber.set_scheduler(nil).
Pros:
- Uses only built-in Ruby methods 
Fiber.set_schedulerandFiber.schedule. 
Cons:
- No compatibility with other fiber schedulers.
 
This example runs two HTTP requests in parallel:
require "fiber_scheduler"
require "open-uri"
FiberScheduler do
  Fiber.schedule do
    URI.open("https://httpbin.org/delay/2")
  end
  Fiber.schedule do
    URI.open("https://httpbin.org/delay/2")
  end
endThis example runs various operations in parallel. The example total running time is slightly more than 2 seconds, which indicates all the operations ran in parallel.
Note that all the operations used in Fiber.schedule blocks below are either
common gems or built-in Ruby methods. They all work asynchronously with this
library, no monkey patching!
require "fiber_scheduler"
require "httparty"
require "open-uri"
require "redis"
require "sequel"
DB = Sequel.postgres
Sequel.extension(:fiber_concurrency)
FiberScheduler do
  Fiber.schedule do
    # This HTTP request takes 2 seconds (slightly more because of the latency)
    URI.open("https://httpbin.org/delay/2")
  end
  Fiber.schedule do
    # Use any HTTP library
    HTTParty.get("https://httpbin.org/delay/2")
  end
  Fiber.schedule do
    # Works with any TCP protocol library
    Redis.new.blpop("abc123", 2)
  end
  Fiber.schedule do
    # Make database queries
    DB.run("SELECT pg_sleep(2)")
  end
  Fiber.schedule do
    sleep 2
  end
  Fiber.schedule do
    # Run system commands
    `sleep 2`
  end
endEasily run thousands and thousands of blocking operations in parallel. This program finishes in about 2.5 seconds.
require "fiber_scheduler"
FiberScheduler do
  10_000.times do
    Fiber.schedule do
      sleep 2
    end
  end
endGotcha: be careful about the overheads when scaling things. The below snippet
runs sleep which is an "inexpensive" operation. But, if we were to run
thousands of network requests there would be more overhead (establishing
TCP connections, SSL handshakes etc) which would prolong program running time.
It's possible to nest Fiber.schedule blocks arbitrarily deep.
All the sleep operations in this snippet run in parallel and the program
finishes in 2 seconds total.
require "fiber_scheduler"
FiberScheduler do
  Fiber.schedule do
    Fiber.schedule do
      sleep 2
    end
    Fiber.schedule do
      sleep 2
    end
    sleep 2
  end
  Fiber.schedule do
    sleep 2
  end
endSometimes it's conventient for the parent to wait on the child fiber to
complete. Use Fiber.schedule(:waiting) to achieve that.
In the below example fiber labeled parent will wait for the child fiber to
complete. Note that only the parent fiber waits, other fibers run as usual.
This example takes 4 seconds to finish.
require "fiber_scheduler"
FiberScheduler do
  Fiber.schedule do # parent
    Fiber.schedule(:waiting) do # child
      sleep 2
    end
    # The fiber stops here until the waiting child fiber completes.
    sleep 2
  end
  Fiber.schedule do
    sleep 2
  end
endBlocking fibers "block" all the other fibers from running until they're finished.
This example takes 4 seconds to finish.
require "fiber_scheduler"
FiberScheduler do
  Fiber.schedule do
    Fiber.schedule(:blocking) do
      sleep 2
    end
  end
  Fiber.schedule do
    sleep 2
  end
endVolatile fibers end when all the "durable" fibers finish. Volatile fibers (by design) may not complete all their work.
This is useful if you have a neverending task that performs some cleanup work that should finish when the rest of the program completes.
This example takes 2 seconds to finish.
require "fiber_scheduler"
FiberScheduler do
  Fiber.schedule(:volatile) do
    # This fiber will live for only 2 seconds.
    loop do
      cleanup_work # this method will run only once
      sleep 10
    end
  end
  Fiber.schedule do
    sleep 2
  end
endasync is an awesome asynchronous programming library, if not a framework.
If async is like Rails, then fiber_scheduler is plain Ruby.
fiber_scheduler is fully compatible with async:
Async do |task|
  task.async do
    # code ...
  end
  FiberScheduler do
    Fiber.schedule do
      # code ...
    end
  end
  # ...
endNote that currently the opposite doesn't work:
FiberScheduler do
  Async do
    # ...
  end
  Fiber.schedule do # No scheduler is available! (RuntimeError)
    # ...
  end
endfiber_scheduler gem works with any other Fiber Scheduler class (current and
future ones). Example:
Fiber.set_scheduler(AnotherScheduler.new)
# stuff
FiberScheduler do
  # works just fine
end
# more stufffiber_scheduler is like choosing pure Ruby: it's a safe choice because you
know it works and will continue working with everything else in Ruby's
asynchronous eco-system.
This basic perf benchmark looks promising.
HINT: make sure to install io-event gem alongside fiber_scheduler for a
performance improvement.
Samuel Williams for:
- Implementing Ruby's Fiber Scheduler hooks.
 - The default selector used in this gem.