Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested define/require inside another require does not see defined module #173

Closed
jrburke opened this issue Jan 23, 2012 · 17 comments
Closed
Milestone

Comments

@jrburke
Copy link
Member

jrburke commented Jan 23, 2012

Found in a test case by @dvdotsenko in this set of tests https://github.com/dvdotsenko/AMDLoaderTests :

require(['a'], function (a) {
    define('b', ['c'], function (c) {});

    require(['b'], function(b) {
        //fails because loader tries to load 'b' from network
    });
});
@AndrewHenderson
Copy link

I'm finding my nested dependencies are passed as undefined. I've used jQuery twice in this sample simply to demonstrate, however in my actual code, I am not attempting to call the same dependency on the inner define.

define([
      'jquery'
],
function ( $ ) {

    $ // defined

    define([ 'jquery' ], function ( $ ) {

        $ // undefined

    });

}());

What am I missing in order to have the variable defined on the inner define?

@jrburke
Copy link
Member Author

jrburke commented Jan 1, 2013

Nested defines like that would not make sense. define() is for defining a module. For grabbing a module while inside a define's factory function, require([]) should be used instead.

@AndrewHenderson
Copy link

I tried various implementations. I was unable to get it to work. Basically, I am attempting to dynamically generate dependencies for the inner function.

Is it possible to pass a set of additional dependencies to a nested function?

@jrburke
Copy link
Member Author

jrburke commented Jan 6, 2013

You can use require([]) to dynamically load things inside a define() call. But note that require([]) is async, so it will call the function callback in a later turn, the loader will not call the callback function before the define() factory function completes execution.

define(['require', 'jquery'], function (require, jquery) {
    require(['something/else'], function (somethingElse) {

    });
});

@AndrewHenderson
Copy link

That worked beautifully!

@patrixd
Copy link

patrixd commented May 8, 2013

Hi, I need something like that but a syncronous require. Stop execution and continue after get somethingElse. I can't be sure while I'm loading a module the user ask for another one. Or the developer try to use somethingelse after that.
I need it to get a module dependent of some dynamic conditions, and avoid to load all the posible modules in the first define. How could I do that? Thanks in advance.

@jrburke
Copy link
Member Author

jrburke commented May 8, 2013

@patrixd Synchronous require that does a fetch is not supported. What is possible is preventing further require() calls by waiting on the result of the first call using a mechanism similar to this:

define(['require', 'a'], function (require, a) {
  var queue = [];
  var fetchingId, cb;

  function fetched(moduleValue) {
    cb(moduleValue);

    // Now decide if the other items in the queue
    // should be fetched or discarded.
  }

  function whenClear(id, callback) {
    if (!fetchingId) {
        fetchingId = id;
        cb = callback;
        require([id], fetched);
    } else {
      queue.push([id, callback]);
    }
  }  

  return {
    loadSomething: function (id, callback) {
        whenClear(id, callback);
    }
  }
});

Where the module's loadSomething is used by outside code.

@patrixd
Copy link

patrixd commented May 29, 2013

Thank you for your help.
But I think that it does not work to me because the optimizer does not include the paths that are not included in defines and paths of main-built.js. In local, the require works to me because the files exist, but in production it does not. I don't have another choice to add them in define or config paths?
I am doing something like your example:

define(['require', 'jquery'], function (require, jquery) {
    require(['something/else'], function (somethingElse) {

    });
});

And when the site is loaded there is a 404 error telling that something/else was not found. Do you know what I can do? Thanks in advance.

@jrburke
Copy link
Member Author

jrburke commented Jun 7, 2013

By default, nested require([]) calls inside a define() call are not included in a build as it indicates something that should normally be dynamically loaded. However you can override this by using findNestedDependencies: true in the build config:
https://github.com/jrburke/r.js/blob/master/build/example.build.js#L318

@patrixd
Copy link

patrixd commented Jun 7, 2013

Thank you so much! I didn't know it! I'm going to add that in my config just now. =)

@crishushu
Copy link

@jrburke I had the same wish to have dynamic and synchronous file loading. While trying out various define/require nested constructions, I finally found your statements on this issue. I have a question concerning the mechanism in your 'loadSomething' example. I find out that a require call in the remaining id/cb pairs within the queue is not synchronous anymore. So why we have a synchronous require in the very first call?

@joseph-jja
Copy link

I have been running into an issue on windows, which I can only attribute as some type of race condition and it sounds like it is related to this.

We have code:

define([ .. modules ], function( ... ) {
var somevar;
var foo = Backbone.View.extend({
initaiize: function() {
somevar = this;
},
render: function() {
require([...], function(...) {
//now use somevar
}
}
});
})

in the block of now use somevar, somevar ends up sometimes being undefined. This happens on window :/ while running tests. Assuming initialize() is called before render() and somevar is set, why would this sometimes be null?

@emiglobetrotting
Copy link

nice thread..

@emiglobetrotting
Copy link

emiglobetrotting commented Apr 14, 2017

I wish to get an object from the callback in require and return it as defined module, e.g.

define(['require', 'jquery'], function (require, jquery) {
   var obj;
    require(['something/else'], function (somethingElse) {
         obj = loadedsomething;
         console.log(obj); //expected object is defined
    });
    return obj; //obj is undefined. but I want to get as defined under this scope
});

How can I solve this problem.

@FoC-
Copy link

FoC- commented Apr 20, 2017

You should not add nested resolution for deps. Push them to top.
More info

define(['require', 'jquery', 'something/else'], function (require, jquery, somethingElse) {
    console.log(somethingElse); //expected object is defined

    somethingElse.$ = jquery; // or something else

    return somethingElse;
});

@emiglobetrotting
Copy link

thanks @FoC- got it working.

@mrtuvn
Copy link

mrtuvn commented Jul 30, 2021

Hi @jrburke i just recognised in 2.3.6 version. Require not able to get depend as i expected if i put inside function

define(['require', 'abc', 'something/else'], function (require, abc, somethingElse) {
    //working fine here

    someVar = {
           init: function () {
               require(['package'], function(package) {
                       //package not able to work. I got some message undefined call to something
               });
          },

          update: function () {}
    }
});

For requirements i don't want to put package file to top define. Only require it on demands so i put it in side deeper scope

For real example please reference this link
https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js#L91
got element.datepicker undefined. You can see 2 require block inside each function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants