From f642db015f4b3257fd63dd3a9382312f6adb3608 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Sun, 16 Jul 2017 22:33:15 +1000 Subject: [PATCH] refactor(core/highlight): simplify async code --- src/core/highlight.js | 115 ++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/src/core/highlight.js b/src/core/highlight.js index 9ef1844863..ed06f89f60 100644 --- a/src/core/highlight.js +++ b/src/core/highlight.js @@ -5,90 +5,73 @@ */ import "deps/regenerator"; import ghCss from "deps/text!core/css/github.css"; -import { makeOwnerSwapper } from "core/utils"; import { pub, sub } from "core/pubsubhub"; import { worker } from "core/worker"; export const name = "core/highlight"; + // Opportunistically insert the style into the head to reduce FOUC. -var codeStyle = document.createElement("style"); +const codeStyle = document.createElement("style"); codeStyle.textContent = ghCss; -var swapStyleOwner = makeOwnerSwapper(codeStyle); -swapStyleOwner(document.head); - +document.head.appendChild(codeStyle); +let idCounter = 0; function getLanguageHint(classList) { return Array.from(classList) .filter(item => item !== "highlight" && item !== "nolinks") .map(item => item.toLowerCase()); } -let doneResolver; -let doneRejector; -export const done = new Promise((resolve, reject) => { - doneResolver = resolve; - doneRejector = reject; -}); -export async function run(conf, doc, cb) { +export async function run(conf) { // Nothing to do if (conf.noHighlightCSS) { - doneResolver(); - return cb(); + return; } - - if (codeStyle.ownerDocument !== doc) { - swapStyleOwner(doc.head); + const highlightables = Array.from( + document.querySelectorAll("pre:not(.idl):not(.nohighlight), code.highlight") + ); + if (!highlightables.length) { + return; } - - const promisesToHighlight = Array.from( - doc.querySelectorAll("pre:not(.idl):not(.nohighlight),code.highlight") - ) - .map(element => { + const promisesToHighlight = highlightables.map(element => { + return new Promise(resolve => { + if (element.textContent.trim() === "") { + return resolve(element); // no work to do + } + const done = () => { + element.setAttribute("aria-busy", "false"); + resolve(element); + }; + // We always resolve, even if we couldn't actually highlight + const timeoutId = setTimeout(() => { + console.error("Timed-out waiting for highlight:", element); + done(); + }, 1000); + const msg = { + action: "highlight", + code: element.textContent, + id: "highlight:" + idCounter++, + languages: getLanguageHint(element.classList), + }; element.setAttribute("aria-busy", "true"); - return element; - }) - .map(element => { - return new Promise((resolve, reject) => { - if (element.textContent.trim() === "") { - return resolve(element); // no work to do + element.setAttribute("aria-live", "polite"); + worker.postMessage(msg); + worker.addEventListener("message", function listener(ev) { + const { data: { id, code, language, value } } = ev; + if (id !== msg.id) { + return; // not for us! } - const msg = { - action: "highlight", - code: element.textContent, - id: Math.random().toString(), - languages: getLanguageHint(element.classList), - }; - element.setAttribute("aria-live", "polite"); - worker.postMessage(msg); - worker.addEventListener("message", function listener(ev) { - if (ev.data.id !== msg.id) { - return; // not for us! - } - worker.removeEventListener("message", listener); - const { value, language } = ev.data; - element.innerHTML = value; - if (element.localName === "pre") { - element.classList.add("hljs"); - element.classList.add(language); - } - resolve(element); - }); - setTimeout(() => { - element.setAttribute("aria-busy", "false"); - const errMsg = "Timeout error trying to process: " + msg.code; - const err = new Error(errMsg); - reject(err); - }, 5000); + element.innerHTML = value; + if (element.localName === "pre") { + element.classList.add("hljs"); + } + if (language) { + element.classList.add(language); + } + clearTimeout(timeoutId); + worker.removeEventListener("message", listener); + done(); }); }); - try { - const tranformedElements = await Promise.all(promisesToHighlight); - tranformedElements.forEach(element => - element.setAttribute("aria-busy", "false") - ); - doneResolver(); - } catch (err) { - console.error(err); - doneRejector(err); - } - cb(); + }); + await Promise.all(promisesToHighlight); }