EventMachine
asynchronous file reading
Ruby programming
non-blocking IO
file handling

Read file in EventMachine asynchronously

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

EventMachine is built around an event loop that works very well for sockets, timers, and network protocols. Regular file I/O is different: on most systems it is still blocking, so the normal way to read a file "asynchronously" in EventMachine is to move the blocking file work off the reactor thread and deliver the result back through a callback.

Why File Reads Are Different From Socket Reads

A common misunderstanding is that EventMachine can make any I/O operation non-blocking just because it runs inside an event loop. That is true for many network operations, but it is not how regular disk file reads usually behave.

If you call File.read directly inside EM.run, the reactor thread blocks until the file has been read. During that time, timers, network callbacks, and other reactor work are delayed.

That is why the usual EventMachine answer is EM.defer.

Use EM.defer for Blocking File Work

EM.defer runs a blocking operation in EventMachine's worker thread pool and then schedules a callback back on the reactor thread.

ruby
1require "eventmachine"
2
3EM.run do
4  EM.add_periodic_timer(0.5) do
5    puts "reactor still responsive"
6  end
7
8  EM.defer(
9    proc do
10      File.read("example.txt")
11    end,
12    proc do |contents|
13      puts "file size: #{contents.bytesize}"
14      EM.stop
15    end
16  )
17end

This is the standard pattern:

  • the file read happens off the reactor thread
  • the callback receives the result back on the reactor thread
  • the event loop can keep handling other events in the meantime

That is asynchronous from the application's point of view, even though the underlying file operation itself is still a blocking call in a worker thread.

Handle Errors Explicitly

If the file may not exist or may be unreadable, catch the error inside the deferred operation and pass back a structured result.

ruby
1require "eventmachine"
2
3EM.run do
4  EM.defer(
5    proc do
6      begin
7        { ok: true, data: File.read("missing_or_real.txt") }
8      rescue => e
9        { ok: false, error: e.message }
10      end
11    end,
12    proc do |result|
13      if result[:ok]
14        puts result[:data]
15      else
16        warn "read failed: #{result[:error]}"
17      end
18      EM.stop
19    end
20  )
21end

That keeps exceptions from escaping unpredictably across the thread boundary.

Read Large Files in Chunks When Needed

For large files, reading the whole file into memory may be the wrong design even if it is deferred. A better pattern is to stream chunks in the worker thread and pass progress back carefully.

ruby
1require "eventmachine"
2
3EM.run do
4  EM.defer(proc do
5    File.open("big.txt", "rb") do |file|
6      until file.eof?
7        chunk = file.read(1024)
8        EM.schedule do
9          puts "received chunk of #{chunk.bytesize} bytes"
10        end
11      end
12    end
13    :done
14  end, proc do
15    puts "finished"
16    EM.stop
17  end)
18end

EM.schedule is used here to marshal work back onto the reactor thread safely.

Know the Limits of the Approach

EM.defer is practical, but it is not magic. If you queue too many large file operations, you can exhaust the worker pool or shift the bottleneck from the reactor to the thread pool.

That means you should treat deferred file reading as controlled offloading, not as unlimited parallel I/O. If your application is heavily file-oriented rather than network-oriented, another concurrency model may fit better than EventMachine.

Common Pitfalls

The most common mistake is calling File.read directly in the reactor and assuming EventMachine will make it non-blocking.

Another mistake is reading huge files into memory in one shot when chunked processing would be safer.

A third issue is forgetting that callbacks run on the reactor thread, so shared mutable state still needs disciplined handling.

Finally, do not confuse "asynchronous in my application flow" with "native kernel-level non-blocking file I/O." In EventMachine, ordinary file reads are usually asynchronous by delegation to worker threads.

Summary

  • EventMachine does not make ordinary file reads magically non-blocking on the reactor.
  • The normal pattern is EM.defer for the blocking read and a callback for the result.
  • Use structured error handling so failures are returned cleanly to the callback.
  • For large files, prefer chunked processing over File.read of the entire file.
  • Be aware of worker-pool limits when deferring many file operations.
  • In EventMachine, asynchronous file reading usually means offloading blocking work, not changing the nature of file I/O itself.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.