Slides of Oleksandr Khokhlov, Founder at Nots.io, at Elixir Club Ukraine, Kyiv, 28.09.2019
Next conference - http://www.elixirkyiv.club/
Description of presentation
Elixir and Erlang platform are all about asynchronous communications. Why don’t we use this feature while rendering templates? In this talk, Oleksandr unveils how we in Nots.io split HTML documents into smaller pieces and render them in parallel.
Follow us on social networks @ElixirClubUA and #ElixirClubUA
Announce and materials from conf - https://www.fb.me/ElixirClubUA
News - https://twitter.com/ElixirClubUA
Photo and free atmosphere - https://www.instagram.com/ElixirClubUA
*Organizer’s chanel - https://t.me/incredevly
19. But first
iolist() | iodata()
Does not allocate new strings: Repeated
chunks are created only once
Does not concatenate strings/chars: Append
to the list is O(1) and doesn’t require copying
anything.
03 • Phoenix templates
20. Use iolist for I/O
iolist() | iodata()
With iolist & iodata work:
Console I/O functions
File I/O function
:gen_tcp.send()
:gen_udp.send()
03 • Phoenix templates
22. At compile time Phoenix runs through the templates folder
Uses EEx engine to convert each template
into a function
phoenix/lib/phoenix/template.ex
03 • Phoenix templates
29. At the end
of the day iex(1)> PageView.render("index.html", hello: "Elixir!")
{:safe,
[“<body>n <h1>Hello, n", "Elixir!", " </h1>n</body>n"]}
iolist
In runtime
03 • Phoenix templates
31. What if we
return Task 🤔
Instead of HTML
04 • The solution
task = Task.async(fn -> do_heavy_work() end)
IO.puts "Do some more work"
result = Task.await(task)
32. What if we
return Task 🤔
in pseudocode
04 • The solution
{:safe, [["" | "<div class="branch-selector"""> "],
["" | Task.async(fn -> render_dropdown() end)] ,
["" | "n</div>nn"]]}
33. And when
everything is
ready — convert
04 • The solution
defmodule Notsapp.TemplateRenderer do
def process_rendered_template([h|t]) do
[process_rendered_template(h) | process_rendered_template(t)]
end
def process_rendered_template(%Notsapp.CljTask{task: task}) do
Task.await(task)
end
def process_rendered_template(e) do
e
end
defmacro __using__(_opts) do
quote do
def render(template, assigns) do
{:safe, io_list} = render_template(template, assigns)
{:safe, process_rendered_template(io_list)}
end
end
end
end
34. Apply to
every view
04 • The solution
def view do
quote do
use Phoenix.View
use Notsapp.TemplateRenderer # <--
use Phoenix.HTML