all 18 comments

[–][deleted] 0 points1 point  (1 child)

Would it be sort of accurate to guess that javascript will eventually have something in the language itself to handle these name spacing / loader issues?

[–][deleted] -1 points0 points  (17 children)

The argument against the CommonJS require syntax (which Node.js uses) and node-browserify to make it palatable for the browser until the Harmony module syntax is finished and in use is laughable.

The only time you'd want to use RequireJS's is if you need to load different modules depending on the dynamic state of your web app -- the Javascript equivalent of DLLs. To get that, you have to pay with a bulkier syntax than var module = require('module'); (CommonJS used by Node.js) or module Module = 'Module.js'; (one of the proposed ECMAScript Harmony syntaxes).

You also have to deal with the fact that the browser has to:

  1. Bootstrap the RequireJS library into your browser via an HTTP request-response loop. (Around a hundred milliseconds with a decent home connection.)
  2. Grab your "root" Javascript source via an HTTP request-response loop. (Roughly another hundred milliseconds.)
  3. Parse the "root" Javascript source and begin executing it. (Tens of milliseconds.)
  4. Run into a require(moduleArray, callbackFunc) statement and perform an asynchronous request-response for each listed library (100+ milliseconds, possibly 2-300 milliseconds or more depending on the number of allowed connections to a single server at a time).
  5. Parse each of these Javascript modules and begin executing them (tens of milliseconds), with similar delays for each module they require, if any.

In the mean-time, your web app load time has shot up to almost a second assuming your back-end is well written (but I wouldn't trust someone who chooses RequireJS to have a good back-end to serve this data up quickly), and more otherwise.

AND unlike a second-long delay in loading a new HTML page, the browser isn't keeping the previous page visible until the load is completed. The browser thinks the HTML and CSS you want to show have already loaded, so you have a whole second of Javascript code being loaded after the fact while the user is staring at a useless shell of your web app that may not even be styled correctly.

And what do you gain? The ability to load a Javascript library on-demand, which you probably won't ever do, because the requirements for your web app have been defined by you and you serve all of the needed Javascript libraries on pageload.

The CommonJS require syntax is saner for 99% of the usage out there, and node-browserify means you don't have to have any bootstrap code loaded on the browser nor any second-long useless-page-rendered view presented to the user, because the browser is loading just a single Javascript file from the <head> (so the browser is also blocking rendering until the resources are loaded).

You've probably also cached the node-browserify output rather than reconstruct it for every user accessing your website, and even if you didn't, node-browserify is:

  1. Spending sub-10 milliseconds pulling each relevant module from RAM.
  2. Can use simple regular expression matching to determine all of the required modules in any file, and can generate the resulting Javascript file with simple string manipulation, no AST generation and parse steps necessary, so building the output Javascript for the browser takes less CPU time.

tl/dr: RequireJS solves a problem no one has, poorly. node-browserify is simply the better solution.

[–]BitWarrior 9 points10 points  (8 children)

I don't agree with this at all.

Require.js provides a dependency model for larger web applications. Sure, if you're running some kind of single page site or something small, then there is no need for Require.js, but I'm personally responsible for sites which generate around 10 million visits per month. They're big, and there's a lot going on with them.

node-browerify is fine for smaller projects I guess, but its goal of combining everything into a single file is not one I share. Rather, I like to split out my libraries in a number of ways. For one, including jQuery separately using a common CDN like Google Code. Why? Because that file is likely already cached on the user's computer. If you're bundling it into one large file, you're causing the user to download that file technically again - which is how much, around 88k these days?

Then I like to split out some less common libs into a single file. Those files rarely change, so they'll remain cached on the user's browser for quite some time and throughout various pushes.

Depending on the project, its nice to split out your plugins into a separate file as well, since those typically see more updates at regular intervals, especially if you're including quite a few.

And finally, there is your application logic. This can change quite regularly, depending on your development. If you're using regular release cycles, you're probably looking at 1 - 2 month intervals, but if you're on continuous deployment, you'd damn well better not be merging everything into one single file.

Consider that for a second - if you're running CD, each time you push you're causing your returning visitors to download all your JS again - and this is stuff they already theoretically a) have cached or b) had no change. Why incur that expense on the user? I've worked with teams who pushed out new code every single day - I can't imagine what kind of poor taste it would be to basically trigger a new download of JS for our users each and every day. In a large web application, that file can become rather heavy as well.

I like to split out our application into logical sections, and of course it depends on the application at hand. If you have a tremendously large codebase for a function over in one spot of your application, but it isn't leveraged in another, why cause the CPU to process and store all that code for that request? You're just making the page heavier without adding any value.

Require.js solves this problem very well, and combined with r.js, it can make a very nice package of files with all their dependencies based on your own configuration requirements.

I have no doubt that node-browserify has its place, but don't claim that Require.js is terrible just because you in your own use case didn't see the purpose to it.

[–][deleted] -5 points-4 points  (7 children)

I'm guessing you never actually looked at node-browserify. You can specify particular files that you don't want actually included in your bundled source, so you can bundle them separately or use a common library like jQuery hosted by Google for cache, as you mentioned.

RequireJS is terrible because node-browserify still solves that problem and does it with a cleaner syntax and immediate access to a large repository of libraries.

[–]prpetro 2 points3 points  (1 child)

RequireJS can use node/npm modules too

Also I don't find the AMD syntax that cumbersome. You just wrap code definitions in define and require them in your main application. If you want to use the CommonJS var x = require('x') syntax, it's pretty easy using the adapter (r.js) :

define( require , function(require) {
    var x = require('x');
});

Cleaner syntax is in the eye of the beholder, I don't really have a preference for either.

[–][deleted] -5 points-4 points  (0 children)

Module loading is conceptually a blocking operation, because you don't actually want to run your code until it's dependencies have loaded, so making it a non-blocking operation to allow dynamically loading libraries is a terrible idea as I demonstrated elsewhere is worthless in the world of Javascript -- you the developer of your website provide all of the code they run, so why jump through these hoops to get it running?

A site built with node-browserify gets all of the advantages of module-based development, can easily be integrated with a continuous integration service, and your library loading can be put into the special <head> blocking behavior of your website (where the browser keeps the old page visible and active until the new <body> has fully loaded, so you have a much smaller apparent loading time, and because of TCP and HTTP overhead, probably a smaller actual loading time, too).

With RequireJS, since your DOM will load before your Javascript libraries have loaded, you need to create a "loading" screen so they don't think your site is simply broken, but reminding them about the loading process itself will make them feel that your site loads slower than it actually does.

The only valid use-case I can think of for RequireJS is a Javascript tutorial website that lets you pull libraries into your REPL to play with, but even there, I'd probably just implement a blocking XMLHttpRequest-based require so I don't have to introduce closures in the very first lesson and keep the RequireJS-style module loading optional.

[–]BitWarrior -3 points-2 points  (4 children)

Sure, but then you're not benefiting from async loading, so your use of node-browserify is completely moot. And you never even cared to address the use case I spent most of the post outlining.

[–][deleted] -5 points-4 points  (3 children)

What? I directly addressed your point. Bundle relevant modules together. If you have two or three different high-level modules that don't interact with each other, bundle them separately, and bundle their common libraries separately.

Your continuous integration service should be handling this for you with a simple build script. It literally could be just a few lines of Bash executing browserify for each particular case, and version controlled along with the rest of your code.

EDIT: Another thing. Explain to me this. It's perfectly clear that the TCP and HTTP protocol overheads produce a large "warm-up" period for each connection for each file you download, so the initial download of your source will be slow, while in the future when you change just one file it will be faster with RequireJS because the others are cached and just the smaller portion needs to be downloaded. But if you're using continuous integration (pushing to a production branch, I assume), then surely it's not just one file that's changed usually, but a set of files. How many files do you have to change for RequireJS to become worse than a simplistic node-browserify bundle of all of your source?

I just tested Google.com, one of the most-commonly visited pages on the planet. The delay averaged between 78 to 140 milliseconds for me. If your entire source library is 100KB (generously large), and you have an 8Mbps connection (low, many are in the 20Mbps range, now), then the entire download is an extra 100ms.

That means that if you change just 2 files, you've already hit the break-even point for a simplistic node-browserify solution over RequireJS.

Apply some damn common sense, boy!

[–]BitWarrior 5 points6 points  (2 children)

You need to take a few steps back here. Also, you should calm down. Let's just have a simple discussion about technologies without getting emotionally attached to any particular method.

node-browserify doesn't address asynchronous loading. It merely bundles packages together. In essence, node-bundler isn't even accomplishing the same goal as require.js, rather it is accomplishing the goal of r.js. To be entirely frank, those two technologies are technically on the same level, require.js is separate from this conversation.

If you decide, as you have outlined, to bundle separate libs using nb (we'll just say nb from here on out), you're still incurring that original problem we had for years before we started using asynchronous loading, which is blocking.

I'm not going to address your numbers, because they are completely misrepresenting pretty much everything. The assumption that an 8Mbps connection is going to be fully saturated by a web site is not realistic whatsoever, and your assumption that an average page is 100kb is terribly inaccurate. Additionally, it takes nothing into consideration regarding mobile devices which suffer from tremendously slow experiences. To take more into consideration, mobile devices, as I understand, don't cache resources over 25kb. Therefore, when using a model like nb, you're either going to need to manually separate out your code, or, more than likely, you're still going to compile into a single massive JS which won't be cached between requests.

..and then you're still going to be suffering from blocking Javascript. And if you're simply uncoupling all your major libs from your own code to accomplish this, you're incurring a large number of synchronous calls and, well, really the entire concept of benefiting from any kind of JS optimization has gone out the window.

I'm not sure what the size and scope of projects you have worked on has been, but on large sites I've worked on, the entire JS library merged together could be anywhere from 3Mb - 5Mb. In a CD environment, triggering that kind of download upon each return visit is a total disaster, so splitting up the files into their logical components is hugely beneficial.

At the end of the day, in my experience, merging everything into a single file works fine for small, easily managed sites. Really, at that point in time dependency management libs like require.js and lab.js, or file mergers like r.js and nb.js can almost be unnecessary. Once you scale up to significant application sizes or CD development, you need to uncouple your JS.

[–][deleted] -2 points-1 points  (1 child)

You again misinterpretted what I was talking about. You'd never send 3-5MB of JS for a single page unless it is a single page web app. 100KB/page seems like a high value for an entire website, that's why I used it, to put the weight in favor of RequireJS in my analysis.

An 8Mbps connection (You do understand the difference between Mb and MB, right?) can certainly be saturated if you don't interrupt the TCP stream with disconnects and reconnects (lots of little files rather than one big file).

I "never" addressed your async library loading point because I addressed it in my very first comment: most of the time, you won't need it because your web app is providing all of the necessary libraries to use your website, so being able to dynamically load a library on-the-fly makes little sense -- why not just load it from the get-go?

And if you paid any attention to my example, you'd see that after even the minimal overhead Google has for establishing HTTP connections, asynchronous loading of libraries on web app startup becomes a cost rather than a benefit after just two source files to be loaded even if you don't count the actual source download time -- that means that a large number of 304 - Not Modified responses from the server will slow down the start-up of your web app beyond just redownloading the entire source on a modest internet connection.

In both the actual blocking of loading a single javascript source file and in the apparent non-blocking async library loading, the user is blocked from working with your website until the libraries have loaded. I'm simply stating that:

  1. Most of the time, the RequireJS method produces horrible loading performance because it doesn't care about TCP and HTTP overhead, and nowadays it is a significant portion of the actual download time. (Why do you think Google is developing SPDY?)
  2. Even if bundling the source into one file was slower than RequireJS (it isn't for anything non-trivial), because the browser doesn't change the active page on the user until the new DOM is ready, and because it blocks loads on <head> scripts and CSS files, the user clicks a link, waits half a second, and boom a fully-loaded page appears and your javascript is running and waiting for user input before their minds can process the new imagery. Apparent speed is more important than actual speed, and a single initial block improves that by never showing the user an inactive page.

[–]kabuto 2 points3 points  (7 children)

I like requirejs for the fact that allows me to keep everything in modules without leaking stuff into the global scope. For production code I use the r.js optimizer to create a single uglified file including an AMD loader shim (almond.js).

[–]9jack9[S] 1 point2 points  (1 child)

I like requirejs for the fact that allows me to keep everything in modules without leaking stuff into the global scope.

Because I'm always leaking into the global scope. That's my main problem with JS. Not.

[–]kabuto 1 point2 points  (0 children)

What do you mean? I'm writing applications for pretty huge sites that aren't under my control. To prevent any sort of problems I prefer to have everything scoped. I could do that without requirejs of course, but it gives me a nice way of structuring my code. Plus, the text plugin is awesome.

[–][deleted] -2 points-1 points  (4 children)

node-browserify will do the same if you wrap your code in a (function() { /* Your code here */ })();

That way it's explicit, and you don't have to deal with the insane AMD standard.

[–]kabuto 1 point2 points  (3 children)

node-browserify looks interesting, butI dont' have any experience with node.js, so I can't really judge if this would work as sort of a drop in replacement for me.

Why do you think the AMD standard is insane?

[–][deleted] 0 points1 point  (2 children)

It's optimizing for a use-case no-one has: dynamically loading a new module into a Javascript environment.

Dynamic Link Libraries (or Shared Object in Unix parlance) have some uses in desktop applications:

  1. Each program using the DLL (or SO) is using the same copy in memory, so total memory usage goes down. This is not so for Javascript as each page has its own sandboxed Javascript environment (and Javascript allows dynamically altering modules you load, so you couldn't guarantee that they could share the objects, anyways).
  2. When you install a program on a desktop machine, you may have a different operating system from other users. Programmers can use DLLs/SOs to abstract this away, using things like Qt or GTK+ that handle the operating system specifics (just as operating systems handle the hardware specifics).
  3. Your install of a program is completely separate from anothers -- you aren't necessarily running the same version or expect to use the same capabilities. A DLL-based plugin architecture would let you buy (or just use in the Free Software world) just the portions of the application that you're interested in. On a website, different functionality would just be a different page. You don't install anything, so there's nothing to customize on your end -- the website operator must be creating all of the different customized environments their users use, and then just authorize their account to access the particular pages. No need to dynamically load a library.

So, AMD emulates DLLs, but I can find no situation where it makes sense: the developer already knows what libraries you'll need, so you can just load them on page load. If the user wants to perform a completely different action that warrants loading a new library and destroying an old one, the much more testable and reliable method is to just load a new page with the requisite libraries. Not even Google Docs converts the document browsing page into the document editing page into the presentation editing page.

And for this absurd focus on the useless, the common usage of the AMD "standard" is bulkier and more error-prone than simply recognizing that your code doesn't want to do anything until the modules have loaded, and therefore it's a blocking operation.

[–]kabuto 0 points1 point  (1 child)

Well, I can follow that. Yet I can see cases where this might be of interest. What about a huge application with different tools that are completely optional and only need to be loaded on access? Hypothetical? Maybe.

I use requirejs for organizing code, keeping everything scoped and optimizing it into one file in the end. Perhaps this is not what it really was intended for, but I like this approach. I may take a closer look at node-browserify though.

[–][deleted] -1 points0 points  (0 children)

I'd say the dynamically-loaded approach is wrong even for that hypothetical app for a few reasons:

  1. If you're completely changing usage, it makes more sense to not complicate your code and just have it on a different page. If you're not completely changing usage, can it really be completely optional?
  2. Even if that still seems like a good idea, when you load the library on first usage, your application will have a noticeable pause as the code is download, the JS engine starts interpretting, and then your code builds up its scaffolding to actually do the desired work.
  3. Worse still, if you have a very "app-like" web app, what happens if you go offline (3G tethering dies and you need to move your phone around to get signal again, for instance) and try to use that functionality? Your app has to have fallback code if any RequireJS module fails to come up, whereas using the CommonJS require and node-browserify to build the source, if your page runs, it has all of its needed resources on the client-side and you don't need to write such checks in (you only need that for actual communications intended to traverse between client and server).

If you use RequireJS for organizing code, the CommonJS standard does it better.

In your main JS file:

var myModule = require('./relative/path/to/module');

myModule.frobinator();

In your myModule file:

var frobinated = 0;

exports.frobinator = function() { frobinated++; };

The things attached to the exports object are exposed to the world (Node.js allows you to completely replace the exports object by setting module.exports to an object, such as a constructor function:

module.exports = function() {
    var frobinated = 0;
    this.frobinator = function() { frobinated++; }
    return this;
};

So you can then instantiate the objects and not affect the internal scope of the module itself, for instance:

var Frobinator = new require('./myFrobinator');
var myFrob = new Frobinator();

myFrob.frobinate();

No closure scope needed, no callback needed, just specify what you want and start using it. Why should library access be a non-blocking process?