Loading times after a few reviews are too long

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