For their example, you could also just do the database lookups asynchronously in the controller, which is good performance practice for queries that don't absolutely need each other. For example, similarly to their example, let's say you start with something like movies, which each have many movie ratings, and your controller looks like this:<p><pre><code> @movie = Movie.find(params[:id])
@ratings = @movie.movie_ratings # or equivalently, @ratings = MovieRating.where(movie_id: @movie.id)
render
</code></pre>
The second line above needs to do the movie lookup, in order to get its ratings; and the second line basically constructs a SQL query that looks up records from the movie_ratings table using the movie_id foreign key that joins them. However, you already have the movie_id before you look up the movie, because it's in params[:id]. So, you could instead do something like this:<p><pre><code> @movie = Movie.find(params[:id])
@ratings = MovieRating.where(movie_id: params[:id[)
render
</code></pre>
However, Ruby being synchronous by default, the above will still do the same thing and take the same amount of time, since line 2 will synchronously wait for line 1 to finish. But now that you've disentangled the query for line 2 with the results from line 1, you can now explicitly do them asynchronously:<p><pre><code> [].tap do |threads|
threads << Thread.new do
@movie = Movie.find(params[:id])
end
threads << Thread.new do
@ratings = MovieRating.where(movie_id: params[:id])
end
end.each(&:join)
render
</code></pre>
I've had projects where I abstracted the above logic to reusable methods like:<p><pre><code> def asynchronously(&block)
@async_actions ||= []
@async_actions << Thread.new do
block.call
end
end
def synchronize
@async_actions.each(&:join)
end
</code></pre>
And then in my controller actions, the previous code becomes:<p><pre><code> asynchronously do
@movie = Movie.find(params[:id])
end
asynchronously do
@ratings = MovieRating.where(movie_id: params[:id])
end
synchronize
render
</code></pre>
Or if that still seems verbose, since each block above is a simple one-liner, you could use alternate syntax like:<p><pre><code> asynchronously { @movie = Movie.find(params[:id]) }
asynchronously { @ratings = MovieRating.where(movie_id: params[:id]) }
synchronize
render
</code></pre>
The thing you have to be careful about when doing direct queries, like above where both queries directly use params[:id], is when you have a situation where authorization to access the data plays a role. For example, if you replace movie with user, and ratings with purchases, now you can't just blindly look up purchases by the user id the untrusted request passed in. You'd want to first validate that the params[:id] being passed is valid for the current user sending the request.<p>You also want to make sure you understand thread safety and the fact that @movie and @ratings above are not contained to their threads (which we're actually using to our advantage here). But in the above example, you replacing a single thread where everything sees everything anyway, with multiple threads where everything still sees everything, so you're not really changing that aspect in the example above anyway.