blog: get rid of Vec allocation in render for good

just spawn the cache whitelist task onto the js ev loop and don't care
about it; the write only need to succeed once and that normally happens
at the very first time.
This commit is contained in:
Peter Cai 2020-04-18 16:46:11 +08:00
parent e0d8c94986
commit 5c2615a6cb
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
1 changed files with 22 additions and 21 deletions

View File

@ -9,10 +9,10 @@ use crate::utils::*;
use js_sys::{JsString, RegExp}; use js_sys::{JsString, RegExp};
use pulldown_cmark::*; use pulldown_cmark::*;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::future::Future;
use std::vec::Vec; use std::vec::Vec;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use wasm_bindgen::closure::Closure; use wasm_bindgen::closure::Closure;
use wasm_bindgen_futures::spawn_local;
// A list of the UUIDs of all published blog posts // A list of the UUIDs of all published blog posts
// This should be SORTED with the newest posts at lower indices (closer to 0) // This should be SORTED with the newest posts at lower indices (closer to 0)
@ -215,16 +215,25 @@ impl PostContentCache {
Some(cache) Some(cache)
} }
async fn transform_tag<'a>(tag: &mut Tag<'a>) { fn transform_tag<'a>(tag: &mut Tag<'a>) {
match tag { match tag {
Tag::Image(_, url, _) => { Tag::Image(_, url, _) => {
// Convert all external image to our cached URL // Convert all external image to our cached URL
// to protect users and speed up page loading // to protect users and speed up page loading
let url_encoded: String = js_sys::encode_uri_component(url).into(); let url_encoded: String = js_sys::encode_uri_component(url).into();
// Also write this URL to whitelist // Also write this URL to whitelist
// (just throw the task onto the JS ev loop,
// because to make this function async we MUST need to
// allocate Vec later in the render function)
// we don't care about if this write succeeds or not, // we don't care about if this write succeeds or not,
// because even if it breaks we still can recover by a simple refresh // because even if it breaks we still can recover by a simple refresh
let _ = store::put_str(&Self::url_to_cache_whitelist_key(url), "Y").await; // and once it's written, it's permanent, so we expect the write
// to succeed as soon as the article is submitted
let url_cache_key = Self::url_to_cache_whitelist_key(url);
spawn_local(async move {
let _ = store::put_str(&url_cache_key, "Y").await;
()
});
// Now we can overwrite the tag URL // Now we can overwrite the tag URL
*url = format!("{}{}", IMG_CACHE_PREFIX, url_encoded).into(); *url = format!("{}{}", IMG_CACHE_PREFIX, url_encoded).into();
}, },
@ -233,17 +242,15 @@ impl PostContentCache {
} }
fn transform_tags<'ev>( fn transform_tags<'ev>(
parser: impl 'ev + Iterator<Item = Event<'ev>> parser: impl Iterator<Item = Event<'ev>>
) -> impl 'ev + Iterator<Item = impl Future<Output = Event<'ev>>> { ) -> impl Iterator<Item = Event<'ev>> {
parser.map(|mut ev| { parser.map(|mut ev| {
async { match ev {
match ev { Event::Start(ref mut tag) | Event::End(ref mut tag) => {
Event::Start(ref mut tag) | Event::End(ref mut tag) => { Self::transform_tag(tag);
Self::transform_tag(tag).await; ev
ev },
}, _ => ev
_ => ev
}
} }
}) })
} }
@ -329,17 +336,11 @@ impl PostContentCache {
let parser = Parser::new_ext(&post.content, Options::all()); let parser = Parser::new_ext(&post.content, Options::all());
// Apply code highlighting via Highlight.js // Apply code highlighting via Highlight.js
let parser = Self::transform_code_block_highlight(parser); let parser = Self::transform_code_block_highlight(parser);
// Apply async tag transform (resulting in an iterator of Futures) // Apply tag transform
let parser = Self::transform_tags(parser); let parser = Self::transform_tags(parser);
// Await on every Future in the queue to convert them back as Events
let mut events: Vec<Event> = vec![];
for ev in parser {
events.push(ev.await);
}
let mut html_output = String::new(); let mut html_output = String::new();
html::push_html(&mut html_output, events.into_iter()); html::push_html(&mut html_output, parser);
html_output = Self::transform_html(html_output); html_output = Self::transform_html(html_output);
PostContentCache { PostContentCache {
uuid: post.uuid.clone(), uuid: post.uuid.clone(),