Is anyone familiar with Yabble or other browser-side CommonJS loaders?
I'm experimenting with Node.js and would very much like to create Javascript modules which can be used interchangeably on the server side and the client side. This may end up being more a "because it's awesome" kind of thing more so than "because it's practical and useful" kind of thing.
As such, I'm basically trying to get the CommonJS require() method to work on the browser-side, which is exactly what Yabble is supposed to do. I don't really know where to start with it though. I can't seem to find any documentation other than what's found in Yabble's Github readme, and that's not helping much.
Essentially all I've done is put this in an HTML page...
<script src="yabble.js"></script>
<!-- Uses require -->
<script>
require.setModuleRoot('http://localhost:8030/')
my_module = require('my_module')
</script>
But anytime I call the require() function all I get a Synchronous require() is not supported. exception thrown.
Can someone help me get started? Where am I supposed to load yabble.js where am I supposed to call require? Is there a special way to run my Javascript modules?
When loading Javascript code that will need to use the require() function into the browser, the entry point into that code must be the require.run() function.
e.g., Good:
<script src="yabble.js"></script>
<script>
require.setModuleRoot('http://localhost:8030/')
require.run('my_module') // <-- Uses require() function somewhere
</script>
e.g., Bad (Will get the Synchronous require() is not supported error):
<script src="yabble.js"></script>
<script src="http://localhost:8030/my_module.js"></script> <!-- <== Use's require function somewhere -->
FYI, it's pretty nifty how Yabble does this. It will actually statically analyze your Javascript source code, I think basically just using a regular expression to look for the require() method, and then try to pull that .js script from the server, then it does the same static analysis of that .js script, and on and on.
This can be particularly confusing because it will actually load those .js scripts even if the control logic would mean program flow would never reach the require() function. e.g., if you had...
if (False) { require('some_module'); }
... Yabble will still load this module.
Synchronous loading of modules in the browser is problematic. A construct like:
var x = require('something_remote.js')
Implies that the browser will halt your code (i.e. block), go and fetch the remote file, parse it, and then return to you the exports. But this does not jive with the single-threaded browser environment - we would be halting the main JavaScript thread (and thus page interactivity for the user) on the performance of the network. So browsers have evolved against this scenario, to favor async loading on their own schedule. There is some good discussion here:
http://www.sitepen.com/blog/2010/07/16/asynchronous-commonjs-modules-for-the-browser-and-introducing-transporter/
One pattern which might work here is that a require() implementation does synchronously block, fetches the file via XHR and then evals it, but that seems to run against all the browser support/infrastructure for async file-based loading. Also I'm curious what implications that would have for the cross-domain primitive of browser security.
So in order to fit the browser async loading model, we'll need to use a callback mechanism like:
require("something.js", function () { // called later, after something.js has loaded! })
It looks like RequireJS is doing this:
http://requirejs.org/docs/start.html
Perhaps give that a shot?
JavaScript environments like NodeJS etc - built with the provision of loading "local" modules from disk, instead of foreign network hosts - can do the synchronous load.
I would greatly appreciate any corrections from JS experts :-)
Related
In a script associated with an Orchard view I try to post an AJAX to server with dojo.request.post function. However, I only get esri/request, dojo/request is undefined. I call the request outside the function where the require statements reside, but there's no problem with other required packages as long as I use them in correct format. Dojo/request works in our other project, so I suspect Orchard of messing things up (the other project's dojo/request use is in a plain page, not in a view), though I would expect problems caused by it to surface earlier.
Important parts of the code:
require([ ... "dojo/request", ... ], function (... Request, ...) {
//custom init function contents
})
function sendResults(featureSet) {
//custom code processing the parameter, making uri, JSON and like
dojo.request.post(uri, {
//sending data
})
}
My razor require part in the same file contains:
Script.Require("esri/JavaScriptApi").AtHead();
Script.Require("dojo").AtHead();
Those are defined in resourcemanifest.cs:
manifest.DefineScript("esri/JavaScriptApi").SetUrl("http://js.arcgis.com/3.14/");
manifest.DefineScript("dojo").SetUrl("//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js");
The error I get when I try to run the code:
TypeError: dojo.request is undefined
I've tested it in FireBug and confirmed that dojo/request doesn't exist (the same for variants, like dojo/Request), only esri/request is a function, but it has no post method.
I'm stuck here. Google search lead to "plains of sheer desperation" (page 5+) with no useful output and my co-workers don't know. Does anyone know why I can't see dojo/request and how to get it?
As far as I'm aware, dojo/request does not export a global (as modern AMD modules generally shouldn't need to), so dojo.request would never work in any context.
The appropriate way to use AMD modules is to use them within the body of a require callback (or, even better, organize your own code in AMD modules as well, and use them within a define factory function).
require([ ... "dojo/request", ... ], function (... request, ...) {
request.post(...);
})
Alternately, if you're certain dojo/request has been loaded by the time you want to use it, you could use single-argument require to reference it:
require('dojo/request').post(...);
However, this is typically not ideal and seen as a hack.
Perhaps the Introduction to AMD Modules tutorial would help to better understand optimal AMD usage.
I've been reading about es6 module loaders and I just don't quite understand how it works and am hoping someone can enlighten me.
In the practical workflows link above they have an example like this
System.import('app/app').then(function(app) {
// app is now the Module object with exports as getters
});
No problem with that - I get it. But then I see stuff like this
var $ = require('jquery');
and get really confused. What happens if at the time of this call jquery has not yet been transferred to the browser? Does the thread just spin? Does the browser parse your script behind-the-scenes and reform it into a callback like RequireJs does? Is what it does configurable? Are there specific limitations?
Can someone give me a rundown?
The ES6 Module Loader will fetch the source, determine dependencies, and wait until those dependencies have loaded before executing the module. So by the time the require executes, the dependency is already sitting there waiting to be executed.
When loading CommonJS through an ES6 module loader, we rely on statically parsing out the require statements from the source, and only executing the source once those requires have loaded.
In this way we can support CommonJS in the browser loaded dynamically. Circular references are treated identically to the way they are handled in Node.
The regular expressions parsing out the require are actually pretty reliable and quick, while taking into account comments and surrounding tokens. See https://github.com/systemjs/systemjs/blob/master/lib/extension-cjs.js#L10 for the one used by SystemJS.
There is one remaining limitation with this approach and that is dynamic and conditional CommonJS requires like if (condition) require('some' + 'name') aren't detected properly. This is a necessary cost though to make CommonJS behave as a fully asynchronous module format in the browser.
I'm working on a framework-like structure that uses curl.js as script loader. Modules can be located in either the App or Core directory, which allows me to override Core modules easily from the app. I had a server-side script to search these directories and return the first found file but then I decided that the client-side should be able to work without any help from backend, so now I have a function that tries listed directories until one of them has a file.
After a while I realized that I don't have any way to differentiate between 404 errors and syntax errors. I want the loader to fail if there is an actual error in code but in case of 404 it should just look for the next file and finally fail if no file can be found.
So, is it possible to detect if a script failed(in curl.js, AMD or generally in js) due to 404 and not because of errors in the loaded code?
Because of security concerns, browsers don't provide much information when scripts fail. In fact, IE refuses to even call the "load" event at all. The only information an AMD loader has is whether the define() was called or not.
There might be a way to detect some types of script errors, if you place your own global function call at the beginning of the script file. You can check if that function has been called. You can't detect SyntaxErrors that prevent parsing of the script, but you can detect other types of errors.
It might be possible to return the exact error to the errback function ( curl([]).then(callback, errback)) when the error happens while executing the factory function. Have you verified if this works or not?
We love RequireJS and AMD during development, where we can edit a module, hit reload in our browser, and immediately see the result. But when it comes time to concatenate our modules into a single file for production deployment, there apparently has to be an AMD loader still present, whether that loader is RequireJS itself or its smaller partner “almond” as explained here:
http://requirejs.org/docs/faq-optimization.html#wrap
My confusion is: why is a loader necessary at all? Unless you have very unusual circumstances that make it necessary for you to make require() calls inside of your modules, it would appear that a series of AMD modules could be concatenated without a loader present at all. The simplest possible example would be a pair of modules like the following.
ModA.js:
define([], function() {
return {a: 1};
});
ModB.js:
define(['ModA'], function(A) {
return {b : 2};
});
Given these two modules, it seems that a concatenator could simply produce the following text, and not burden the production server or browser with the extra bandwidth or computation required by either RequireJS or Almond.
I imagine a concatenator that produces (and I am using chevron-quotes «,» to show where the snippets from the two modules above have been inserted):
(function() {
var ModA = «function() {
return {a: 1};
}»();
var ModB = «function(A) {
return {b : 2};
}»(ModA);
return ModB;
})();
This, so far as I can see, would correctly reproduce the semantics of AMD, with a minimum of extraneous glue JavaScript. Is there such a concatenator available? If not, would I be a fool for thinking that I should write one — are there really very few code bases that consist of simple and clean modules written with define() and that never need further require() calls inside that kick off later asynchronous fetches of code?
An AMD optimiser has the scope to optimise more than the number of files to be downloaded, it can also optimise the number of modules loaded in memory.
For example, if you have 10 modules and can optimise them to 1 file, then you have saved yourself 9 downloads.
If Page1 uses all 10 modules then that's great. But what if Page2 only uses 1? An AMD loader can delay the execution of the 'factory function' until a module is require'd. Therefore, Page2 only triggers a single 'factory function' to execute.
If each module consumes 100kb of memory upon being require'd, then an AMD framework that has runtime optimisation will also save us 900kb of memory on Page2.
An example of this could be an 'About Box' style dialog. Where the very execution of it is delayed until the very last second as it won't be accessed in 99% of cases. E.g. (in loose jQuery syntax):
aboutBoxBtn.click(function () {
require(['aboutBox'], function (aboutBox) {
aboutBox.show();
}
});
You save the expense of creating the JS objects and DOM associated with the 'About Box' until you are sure it's necessary.
For more info, see Delay executing defines until first require for requirejs's take on this.
The only real benefit is if you use modules across sections so there's a benefit to caching modules independently.
I had the same need, so I created a simple AMD "compiler" for that purpose that does just that. You can get it at https://github.com/amitayh/amd-compiler
Please note that it has many features missing, but it does the job (at least for me). Feel free to contribute to the codebase.
In case you compile you code with require.js into a single large file for production, you can use almond.js to completely replace require.
Almond only handles the module references management not the loading itself which is no longer needed.
Be careful of the restrictions almond imposes in order to work
There is no reason why there couldn't be a build tool such as the one you propose.
The last time* I looked at the optimizer's output, it converted the modules to explicitly named modules, and then concatenated those together. It relied on require itself to make sure that the factory functions were called in the right order, and that the proper module objects were passed around. To build a tool like you want, you would have to explicitly linearize the modules -- not impossible, but a lot more work. That's probably why it hasn't been done.
I believe** that the optimizer has a feature to automatically include require itself (or almond) into the built file, so that you only have to have one download. That would be larger than the output of the build tool you want, but otherwise the same.
If there was a build tool that produced the kind of output you're asking for, It would have to be more careful, in case of the synchronous require, the use of exports instead of return, and any other CommonJS compatibility features.
*That was a few years ago. 2010, I think.
**But can't seem to find it right now.
I’m currently testing various asynchronous-resource-loaders to see which one I want to use. At the moment Curl is throwing a ‘Promise already completed’ error…and their documentation says ‘this should never happen’.
I “suspect” I must to use a ‘define’ within each file being loaded (hope not). Further, their documentation says Curl can work with non-AMD javascript files. But...I am new to AMD and since Curl is far-faster than the other ones I'm testing...I am willing to put some time into understanding this better.
Lastly...
Even though FireBug shows the errors...all the files STILL LOAD asynchronously! But, BECAUSE there are errors...the 'then' portion of the code never gets called.
So My Questions Are:
Do I have to update all the JavaScript file-objects to be contained in a 'define'? (...hope not)
Can you see any other problem syntactically?
The Head’s Code Looks Like This:
<script type="text/javascript">
///<summary>Configuration</summary>
curl = { baseUrl: 'Includes/JavaScript/' };
</script>
<script src="Loaders/Curl/curl.js" type="text/javascript"></script>
<script type="text/javascript">
function onSuccess() {
}
function onError(ex) {
//alert(ex);
}
require(["MooTools/Core/mootools-1.2.2-core-jm",
"MooTools/mGeneral",
"jQuery/Core/jquery-1.3.2",
"jQuery/Core/jquery.tools.min",
"jQuery/ThirdPartyPlugIns/jquery.tmpl"])
.then(onSuccess, onError);
//require(["jQuery/TopUp/top_up-min"], null);
require(["jQuery/ThirdPartyPlugIns/jquery.dimensions",
"jQuery/ThirdPartyPlugIns/jQuery.Color.Animations",
"jQuery/ThirdPartyPlugIns/jquery.corners.min",
"jQuery/ThirdPartyPlugIns/jquery.tipsy",
"jQuery/ThirdPartyPlugIns/jquery.numberformatter-1.1.0",
"jQuery/ThirdPartyPlugIns/jquery.tipsy"]);
require(["general",
"WindowCenter",
"ThirdPartyPlugin/KeyBoardCapture",
"ThirdPartyPlugin/bsn.AutoSuggest_2.1.3",
"ee/8Ball",
"ee/EE"]);
</script>
Again...I'm sure it is caused by inexperience with AMD-styled code, but I still need help...so any is appreciated.
Typically, you should wrap your modules in a define() or append a define() at the end of the file if those modules have no dependencies. It seems, though, that those modules all depend on jQuery, if not other modules/files.
Since you're not using standard AMD require()/define() protocol, AMD is not really helping you with these modules. Unless you plan to write your own modules using define(), then you could use just about any async loader.
That said, there is a way to make curl.js work with non-AMD modules/files. Use the js! plugin. Here's your first block of files translated to use the js! plugin (note I also added the ".js" ext back on which I like to do with non-modules):
// we load the core files first, then get the next block that depends on them
curl([
"js!MooTools/Core/mootools-1.2.2-core-jm.js",
"js!jQuery/Core/jquery-1.3.2.js"
]).next([
"js!MooTools/mGeneral.js",
"js!jQuery/Core/jquery.tools.min.js",
"js!jQuery/ThirdPartyPlugIns/jquery.tmpl.js"
]).then(onSuccess, onError);
If any of those files within each array depend on each other, you can also use the !order suffix on them to ensure they wait for other files before executing/evaluating (be sure you're setting proper cache headers, though). Actually, the !order suffix is the fastest method as long as there are no caching issues (mobile browsers add some additional constraints on file size).
Were there any other error messages? Specifically, I would expect curl.js to throw at least one error besides just "Promise not completed".
Also, please check the Net tab (Firebug) or the Network tab (Chrome) to check that curl.js is looking in the correct location for the modules.
FWIW, I am planning to remove the alias require --> curl. The reason is that the global require function is non-standard (and explicitly not standardized in the AMD proposal). I suggest you use curl() instead of require().
curl.js also allows it's top-level api to be aliased explicitly via the "apiName" config param if you really, really want to use the name "require". :)
<script>curl = { apiName: "require" }; </script>
<script src="path/to/curl.js"></script>
<script>require(["some/modules"]).then(success, failure);</script>
More FWIW: the standard require is typically only needed in a module and can be requested by including it as a dependency:
define(["require"], function (require) {
require(["another/module"], function (another) {
// use another module here
});
});
-- John
If you're only doing normal javascript file loading (not modules), as it appears, i would encourage you to check out LABjs (http://labjs.com). LABjs focuses on being the most performance optimized loading solution (to the exclusion of some other features like module/dependency style stuff).
In fact, LABjs 2.0a (https://github.com/getify/LABjs/blob/master/next/LAB.src.js), which will be fully released in the next few days, is really exceptionally fast (even more than 1.2) at loading general scripts in parallel. I encourage you to give it a try, unless (as John eludes to above) you plan to go to module syntax... then stick with Curl or RequireJS.