TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Speeding Up Rendering Rails Pages with render_async

127 pointsby nikolalsvkalmost 8 years ago

13 comments

matthewmacleodalmost 8 years ago
That&#x27;s actually a relatively nice, Rails-magic-style approach to solving this sort of thing. Of course, if you were building a more interactive application, you&#x27;d already have a JS framework in place that would negate these benefits – but I&#x27;m still convinced there&#x27;s a nice middle-ground for server-rendered Rails apps that avoids the various problems of SPAs.<p>It would be nice if it worked without JavaScript though – an increasing pain-in-the-arse about the web generally. If only it were possible to have &lt;iframe&gt;s adjust to their content size, then we wouldn&#x27;t need JavaScript at all!
评论 #14514312 未加载
评论 #14514057 未加载
评论 #14514159 未加载
评论 #14514453 未加载
评论 #14514148 未加载
评论 #14515114 未加载
jonathanhefneralmost 8 years ago
I think there is a more Rails-y way to do this. Derek Prior gave an excellent talk[1] at the recent RailsConf. The TLDW is &quot;all-REST all-the-time.&quot; Applying this principal to the example in the article, instead of adding a custom `movie_rating` action, you would create a `MovieRatingsController` with a `show` action.<p>Additionally, you might create an SJR[2] template which injects the rating directly into the page, possibly avoiding the need for a separate gem.<p>[1] <a href="https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HctYHe-YjnE" rel="nofollow">https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HctYHe-YjnE</a><p>[2] <a href="https:&#x2F;&#x2F;signalvnoise.com&#x2F;posts&#x2F;3697-server-generated-javascript-responses" rel="nofollow">https:&#x2F;&#x2F;signalvnoise.com&#x2F;posts&#x2F;3697-server-generated-javascr...</a>
deedubayaalmost 8 years ago
This could be done without a gem relatively easy if you&#x27;re using Rails5+ &amp; ActionCable.<p>Basically generate a uuid to use as a channel identifier, then pass that uuid to the front end to subscribe to. Pass the uuid to your ActionCable background job to do the expensive rendering in a background queue and push the information over the websocket channel. Have the JS insert it appropriately (maybe the contents of the element with an id of the uuid).<p>This would have the benefit of displacing the expensive computation in a background job, which wouldn&#x27;t impact other web requests.
ewalk153almost 8 years ago
I would use a technique like this sparingly. It looks to be the &quot;web request&quot; equivalent of an n+1 query.<p>Imagine you have a table of results and each one is taking time to render. You add this for each row, but you only test it locally with a few rows. In production, maybe that table has hundreds of rows.<p>You&#x27;ve just DOSed your server.
评论 #14514289 未加载
评论 #14514726 未加载
评论 #14514470 未加载
评论 #14514266 未加载
mendelkalmost 8 years ago
If you&#x27;re interested in these types of improvements, you&#x27;ll love intercooler[0]! It&#x27;s basically this, with lots of more options and patterns. It&#x27;s been great for me in the (small-ish) projects I&#x27;ve used it.<p>[0] <a href="http:&#x2F;&#x2F;intercoolerjs.org&#x2F;" rel="nofollow">http:&#x2F;&#x2F;intercoolerjs.org&#x2F;</a>
juliandalmost 8 years ago
It&#x27;s good to see useful libraries still being created for Rails. I plan to use this on one of my projects. Thank you.
Dangerangeralmost 8 years ago
Is it possible to configure render_async to make the request and DOM manipulation without JQuery? Rails 5.1+ will not continue to bundle JQuery with UJS anymore since UJS has been re-written in vanilla Javascript.<p>If it were possible, I&#x27;d find this feature more useful for smaller Rails apps that are in the middle ground between server rendered HTML and full blown SPA.
nateberkopecalmost 8 years ago
Does this work with HTTP caching?<p>i.e. if I use this to render a movie rating asynchronously, can I return a 304 not modified response and the client will insert a previously-delivered response fragment?
评论 #14514891 未加载
raman162almost 8 years ago
I think this with a combination of caching can be very powerful.
jbverschooralmost 8 years ago
why not use fibers or something to do db calls async and then join them at render. Saves you all the network calls
JangoStevealmost 8 years ago
For their example, you could also just do the database lookups asynchronously in the controller, which is good performance practice for queries that don&#x27;t absolutely need each other. For example, similarly to their example, let&#x27;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&#x27;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&#x27;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 &lt;&lt; Thread.new do @movie = Movie.find(params[:id]) end threads &lt;&lt; Thread.new do @ratings = MovieRating.where(movie_id: params[:id]) end end.each(&amp;:join) render </code></pre> I&#x27;ve had projects where I abstracted the above logic to reusable methods like:<p><pre><code> def asynchronously(&amp;block) @async_actions ||= [] @async_actions &lt;&lt; Thread.new do block.call end end def synchronize @async_actions.each(&amp;: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&#x27;t just blindly look up purchases by the user id the untrusted request passed in. You&#x27;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&#x27;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&#x27;re not really changing that aspect in the example above anyway.
评论 #14517688 未加载
voidlogicalmost 8 years ago
So this is an alternative of designing your app FE to use AJAX or using ESI (edge side includes) right? With the caveat this is super rails specific?
alttabalmost 8 years ago
Why is this a gem? It&#x27;s two view helpers and like 30 lines of Ruby code, if that.<p>I&#x27;ve solved this problem the same way probably 5 times in rails apps and it&#x27;s super easy to build this interface in yourself without taking on another dependency
评论 #14514797 未加载
评论 #14514884 未加载
评论 #14514457 未加载