requirejs/almond

Simulate async callback makes main require asynchronous, with no way to suppress it

les2 opened this issue · 6 comments

les2 commented

I ended up commenting out these lines beginning at 293:

    //Simulate async callback;
    //if (forceSync) { // LES: we need this to be synchronous, but there is no way to force it using r.js insertRequire config option; this is a workaround; i'm not sure why almond has this branch in here.
        main(undef, deps, callback, relName);
    //} else {
    //    setTimeout(function () {
    //        main(undef, deps, callback, relName);
    //    }, 15);
    //}

(this code is inside this function):

    requirejs = require = req = function (deps, callback, relName, forceSync) {

The insertRequire option of the optimizer script (r.js) does not allow passing the forceSync parameter. For a use case, I needed to execute everything synchronously in the main module until I have bootstrapped because a script included by another party needs some initialization stuff I export there.

The hack above works and didn't seem to break anything, but I was wondering if someone here would know exactly why the above hack is a bad idea?

The version of almond.js I downloaded was 0.1.2 .

Thanks!

Is this for a top-level require() call (not a require call inside a define())? If so, then this should work:

var a = require('a');

However, in general, top level require([], function () {}) calls should stay async to keep similar execution flows as the source-loaded code via require.js.

les2 commented

I believe I was making a "top-level require" call?

app.js:

(function () {

    ... almond.js here ...

    ... all my modules here ... define(........) x 1000

    // oh, nos - this guy is async (which is okay, generally)
    require(["wapo/app/main"]);
}());

The file is produced by running r.js with a profile more or less like this:

{
  name: 'almond',
  wrap: true,
  include: [
    'wapo/app/main'
  ],
  insertRequire: ['wapo/app/main'],         <------------- want this to be sync :(
  paths: {
    'almond': 'lib/almond/almond',
    'wapo/app/config' : 'wapo/app/config/prod-config'
  }
}

What I noticed stepping through the debugger was that I was going through that async path. Unfortunately, I could not find an option in the optimizer to force the synchronous version of the require call so I had to hack it to pieces.

Is there something I'm missing?

The require([]) form is async. Try require('') instead at the top level. To do that with an almond build, use wrap, but as startFile and wrap.endFile. The startFile can just be:

(function () {

and then the end file can be

    require('wapo/app/main');
}());

then remove the insertRequire from the build config since the wrap.endFile will do the require. Also be sure to use the latest almond.js.

les2 commented

startFile / endFile are sweet! i converted the build to use the array versions for concat'ing external libraries for the "-with-dependencies.js" version of the build.

It would be cool if start / end could take a file name or the actual string - mixed into one (maybe a {content: 'foo'} if you are using mixed mode).

For example, I build several distributions of the project, with the difference in runtime behavior determined by the configuration used at compile time (so I can get handle the various deployments without nasty if statements all over the place):

var layers = [ { 
      layer: 'foo', <------ determines file name; a build {name}.min.js and {name}.full.js for each
      config: {     <----- requirejs optimizer config
    },
  ...               <----- other stuff i want to set for the layer
  },
  ...
];

For some versions, I want to use: insertRequire: ['one', 'two', 'three']

For yet other versions, I use only: insertRequire: ['one']

If I use the wrap: { ... } config, I would need to put the require('one') in a separate file (because I am also the wrap to concatenate other libraries (I guess I could have a file pass to do that, but then I would need to optimize the entire file again).

startFile: [ 'some/lib1.js', 'some/lib2.js', 'var bar = lib2.noConflict(true);', '(function(){' ],
endFile: ['require(['one','two','three']); }());', 'maybe/another/file/here/why/i/know/not.js']

I guess I could just use require('one');require('two');require('three');... to get three sync versions.

Now that I think about it, it's probably not worth it. I can work around it.

+1 for insertSyncRequire as an an option on r.js.

Last night I faced the exact same problem as les2, on uBerscore (an experimental facade over underscore).

uBescore has 'lodash' as a dependency on AMD, but internally all modules use _.

I basically want to have a 'standalone' .js library (along AMD) that can be <script/> loaded after some global '_' is globalized, so I used almond.

Unfortunately the async require([]) breaks it unless I setTimout.

So I intuitively used a 'modified' almond where just line ~354 if (forceSync) becomes if (true), which does the trick!

To day found this issue and tryed the trick with wrap.endFile, it worked.

But why not have an insertSyncRequire or something by default ? Unless somebody read this post, s/he is in trouble...

@anodynos using the latest almond.js, if you do a wrap.endFile, you can have that file added to the end of the build, and that endFile can do the sync require supported by almond (not requirejs) at the top level:

window.globalName = require('moduleName');

Going to close this since this is possible now.