Loading cards should take the same amount of time each review. So, this sounds like accruing of javascript objects caused by the way the desktop reviewer keeps values stored in the main document
/ globalThis
object. For example, if you have things like document.addEventListener
calls, then those would pile up on each review. Triggering that event listener would call all the piled up functions which would of course take more time the more of them there are.
Or something else like that which is increasing the stuff loaded per review.
Is there a way for speeding this up? Like minifying JavaScript, CSS and HTML?
Yeah, minifying would reduce loading time a bit.
Another trick is to use the desktop reviewer’s storing of objects to your advantage by loading javascript functions from files and setting them as variables into globalThis
and then not loading them again if they are present in the variable. The reason this reduces load time is that the javascript is loaded into memory only on the first review and subsequent reviews will be re-using the same variable set on that first load.
So, what you’d do is split any large parts of the javascript you have into functions and put them into separate files in the collection.media
folder. Each file would look like this:
_bigFunction.js
you put in the collection.media folder
export function bigFunction {
// lots of javascript...
}
Then you’d use the javascript import code originally made by kleinerpirat. Here’s my version which has some extra stuff added to deal with dependent scripts and running something after all scripts are loaded but otherwise it’s the same.
Code to import bigFunction
only once per review session
<script>
function getAnkiPrefix() {
return globalThis.ankiPlatform === "desktop"
? ""
: globalThis.AnkiDroidJS
? "https://appassets.androidplatform.net"
: ".";
}
cardScripts = [
{
name: "Some big function",
url: "_bigFunction.js",
init: (moduleObj) => {
globalThis.bigFunction = moduleObj.bigFunction;
}
},
];
var recurseDictLoaded = (dict, curScript) => {
dict[curScript.name] = false;
if (curScript.dependent) recurseDictLoaded(dict, curScript.dependent);
return dict;
};
// init first time ExternalScriptsLoaded
if (!globalThis.ExternalScriptsLoaded) {
globalThis.ExternalScriptsLoaded = cardScripts.reduce(
recurseDictLoaded,
{}
);
}
// Optional, additional functions you run once all scripts are loaded
var onScriptsLoadedFuncs = [];
function onScriptsLoaded() {
for (func of onScriptsLoadedFuncs) {
if (typeof func === "function") func();
}
}
var allScriptsLoaded = () =>
Object.values(globalThis.ExternalScriptsLoaded).every(
(isLoaded) => isLoaded
);
// guard to prevent further imports on Desktop & AnkiMobile
if (!allScriptsLoaded()) {
for (script of cardScripts) {
loadScript(script);
}
} else {
onScriptsLoaded();
}
async function loadScript(script) {
const scriptPromise = import(`${getAnkiPrefix()}/${script.url}`);
console.log(`Loading ${script.name}.`);
scriptPromise
.then(
(moduleObj) => {
console.log(`Loaded ${script.name}.`);
if (script.init) script.init(moduleObj);
// Set this script as loaded
globalThis.ExternalScriptsLoaded[script.name] = true;
if (script.dependent) loadScript(script.dependent);
// If all scripts have finished loading, run final setup
// Since loading is async, we don't when we'll get to this, thus it's checked on every
// script load
if (allScriptsLoaded()) {
onScriptsLoaded();
}
},
(error) =>
console.log(`An error occured while loading ${script.name}:`, error)
)
.catch((error) =>
console.log(`An error occured while executing ${script.name}:`, error)
);
if (globalThis.onUpdateHook) {
onUpdateHook.push(() => scriptPromise);
}
}
</script>
Note
Doing globalThis.someVar = varValue
is the same as doing var someVar = varValue
at the root level of a <script>
; they are global variables. However, to set a global variable when you’re not at the root level but within the scope of an if
or function
, you’d either do
var myVariable;
function someFunction() {
...
myVariable = someValue
}
or
function someFunction() {
...
globalThis.myVariable = someValue
}
How you’d use bigFunction later…
Note, as explained above, you don’t need to do globalThis.bigFunction
<script>
bigFunction()
</script>