decorateCookedElement for each page change?

Your code should work fine. You’ve just stumbled unto a bug in Discourse.

loadScript() doesn’t set the async attribute for the scripts it loads. So, it defaults to async="true" and that misses up your load order. It’s a browser quirk. You have to force async="false" for scripts loaded with JS.

Plugins are smaller, so they load faster than the main bundle, but them being async means they no longer respect the load order - wait for the main bundle to load and execute before executing.

It probably went unnoticed because loadScript is not nested anywhere in core, as far as I know. You would normally bundle files that need to work together. So, to answer your other question. No, there are no Discourse functions that handle that kind of thing.

Your other snippet should work as well. To make it a little bit easier to read, maybe try chaining them without nesting them.

// This goes outside the decorator
const PLUGINS = {
  speed: "https://example.com/foo.js",
  skipBack: "https://example.com/bar.js",
  jumpForward: "https://example.com/baz.js"
};

// Then do your work inside the decorator
loadScript(MEDIA_ELEMENT_SRC)
  .then(() => loadScript(PLUGINS.speed))
  .then(() => loadScript(PLUGINS.skipBack))
  .then(() => loadScript(PLUGINS.jumpForward))
  .then(() => {
    audioplayers.forEach(function (el) {
      new MediaElementPlayer(el, MEDIA_ELEMENT_CONFIG);
    });
  });