Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: wf:commit_content/1 #158

Open
dmsnell opened this issue May 19, 2023 · 5 comments
Open

Idea: wf:commit_content/1 #158

dmsnell opened this issue May 19, 2023 · 5 comments

Comments

@dmsnell
Copy link
Contributor

dmsnell commented May 19, 2023

This is a low-priority idea that came up during some conversations about React streaming and general streaming designs to minimize the latency from requesting a page and receiving the first bytes. In some cases, getting unfinished HTML to the browser can have a measurable impact on page load experience.

The fundamental problem is demonstrated in the following main() function:

main() -> [
	page_header(),
	#panel { body = [ slow_to_compute_function() ] },
	page_footer()
].

In this function the body of the page takes a long time to generate, but the page header and footer are fast. Because we have to wait for main() to fully-complete before sending bytes to the browser this means that a browser sits waiting with no content.

A new function, wf:commit_content(HTML), could split up the rendering so that some HTML can be send while waiting for the rest of the function to compute.

main() ->
	wf:commit_content(page_header()),
	[
		#panel { body = [ slow_to_compute_function() ] },
		page_footer()
	].

In this version of the function we're communicating to nitrogen that the page header is ready to send to the client and there will be no more HTTP headers to follow; that everything else is the rest of the HTML document.

This is a fairly specific optimization but could have relevance even for Nitrogen projects, especially if they send the Nitrogen SCRIPT tags towards the top of the HTML. A browser currently has to wait for the full request to generate, then download it, then load the JS, then render the page.

If such a commit function existed, it would be possible to send the head of the HTML to the browser, including some of the page structure (e.g. navigation menu and logo/header) and, importantly, a list of JavaScript files and stylesheets to load. This gives the browser the opportunity to start fetching the JavaScript files and some images and stylesheets before the server is done creating the page.

I'm not sure if this can be done with all the supported backends, but I believe with cowboy at least it should be possible simply by sending data to the request process and then continuing to send another after main() finishes and returns. Once the initial committed payload sends, however, Nitrogen would have to ignore further calls to change the status code or send headers (wf:status_code/1 and wf:content_type/1 and wf:header/2 etc…).

@choptastic
Copy link
Member

choptastic commented May 19, 2023 via email

@dmsnell
Copy link
Contributor Author

dmsnell commented May 20, 2023

If I understand this correctly, #delay_body{} ends up sending the rest of the HTML via communication after page load. I did experiment with this using wf:comet/1 and yeah that works fine too, gets an even-more progressive page load.

However, one major problem with this (unless I'm misreading #delay_body{}) is that a call to the page via curl demonstrates that you don't get the delayed content unless and until the Nitrogen JavaScript loads.

The approach with wf:commit_content/1 still sends the full page on a single HTTP call; it merely gets some of the response out to the browser earlier on the socket than the rest of the page. This stands in contrast to waiting until the entire page's HTML is ready and then sending it all at once. If you curl it you will still get the entire rendered page on that initial call.

Again, not a priority, but I wanted to toss the idea out here because I think it might be workable, and can help cut down the initial load and render and interactivity for pages.

@choptastic
Copy link
Member

choptastic commented May 20, 2023 via email

@dmsnell
Copy link
Contributor Author

dmsnell commented May 21, 2023

given the way the tags are rendered in wf_tags and wf_render_elements

I think it would be the same way for rendering, as I don't think it would be viable to do this automatically. rather, someone would need to decide, as in my example, that they want to render a part of their content, commit it, and then continue generating additional content.

In your original statement, you mentioned sending a bunch of javascript files, but I misinterpreted that as a bunch of other javascript files rather than just the normal one on page-load.

either way is relevant, as are styles and anything that might get sent via <link prefetch> or <link preload>

cheers!

@choptastic
Copy link
Member

choptastic commented May 22, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants