Require.js (almond.js) Timing Off

2019-06-09 10:37发布

问题:

I'm trying to use Almond.js and the Require.js optimizer to make a standalone file that can be used without Require.js. I've been able to successfully compile the file, and all the code runs, but there's one problem: the code executes in the wrong order.

Let's say my main file (the one that's in the include= option passed to the optimizer) has the following:

console.log('outside');
require(['someFile'], function(someModule) {
    console.log('inside');
    window.foo = someModule.rightValue;
});

and then out in my HTML I have:

<script src="myCompiledFile.js"></script>
<script>
    console.log('out in the HTML');
</script>

Since I'm using almond.js and a compiled file (and therefore there's no file loading going on) I would expect to get the output:

outside
inside
out in the HTML

However, it turns out there's still some asynchronicity going on, because what I actually see is:

outside
out in the HTML
inside

and if I try to check window.foo from the HTML it's not there.

So, my question is, how can I get the code to be more like a normal/synchronous JS file, which runs all its code first before passing things on to the next script block? I can't exactly tell my users "wrap all your code in a window.setTimeout".

回答1:

By default Almond replicates the timing semantics of RequireJS' require call. Since require is asynchronous in RequireJS, it is also asynchronous in Almond. This can be readily observed in the source: Almond uses a setTimeout to schedule the execution of a module even though it could execute it right away. It makes sense because in the general case developers don't expect that the code they've crafted to work asynchronously will suddenly become synchronous just because they are using Almond. It does not matter in every project but in a project where the UI (for instance) should be refreshed in a certain order, changing the timing semantics could break the code.

Two options:

  • As the comment just before the setTimeout states, using require('id') works globally with Almond. This is because once your bundle is loaded, everything is guaranteed to have been loaded (since Almond does not load dynamically).

  • There's also a way to call require with extra arguments. If the fourth argument is true, the call is going to be synchronous:

    require(['someFile'], function(someModule) {
        console.log('inside');
        window.foo = someModule.rightValue;
    }, undefined, true);
    

This can be ascertained by reading the code of Almond. There's no documentation I could find on forceSync (that's the name of the parameter in question) but some bug reports mention it and I've not seen any comment there that it is meant to be a private functionality.