Loading times after a few reviews are too long

I have a huge problem with the following:

I made my own card template with a lot of CSS classes, etc.
They all have individual designs.
After a while, when I already did a few reviews, the loading of the cards takes longer and longer.
Is there a way for speeding this up? Like minifying JavaScript, CSS and HTML?
Does anyone have some tips on this?

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>
4 Likes

If your problem is large amounts of CSS, then I’m not sure how to help with that. The import-from-files trick may not make any difference there, though I haven’t tried importing css files myself.

I think CSS should be quite fast though? It’s generally better to implement any styling with CSS instead of javascript, if possible, as CSS will be much faster. That’s another optimization you could do: see if any styling changes you have done as javascript could actually be done with pure CSS.

2 Likes

Apparently some recent AnkiDroid version made it so that you no longer need to add https://appassets.androidplatform.net to the import path. This means that the entire script import hulabaloo can be vastly simplified to

<script>
var bigFunction;
if (!bigFunction) {
  import("/_bigFunction.js").then(moduleObj => {
    globalThis.bigFunction = moduleObj.bigFunction;
  });
</script>

Edit: Fixed import path missing /
<script src=... and import(...) work a bit differently

3 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.