
Asynchronous Module Definition (AMD) in JavaScript

This Article is Over Ten Years Old...
Things can and do move very quickly in tech, which means that tech-related articles go out of date almost as soon as they have been written and published. If you are looking for up-to-date technical advice or opinion, it is unlikely that you will find it on this page.
You may find that my recent articles are more relevant, and you are always welcome to drop me a line if you have a specific technical problem you are trying to solve.
As a web developer, you're probably familiar with the concept of modular programming; breaking your code into smaller, more manageable modules can improve performance and make it easier to maintain.
Asynchronous module definition, (commonly abbreviated as AMD), is a popular JavaScript design pattern used to define and load modules asynchronously in web applications. AMD is particularly useful in complex web applications that involve multiple scripts and dependencies.
What is Asynchronous Module Definition?
The primary purpose of AMD is to simplify and modularise the code structure, making it more maintainable and reusable. The AMD pattern allows you to define modules in separate files and load them asynchronously, thereby reducing the loading time and improving the performance of the web application. This is something that will eventually be supported natively in‑browser, but for now, this is one of the more thorough and complete patterns. I use it personally regularly, although often see CommonJS used in existing (Node‑based) codebases too.
A Basic Example
In an AMD design pattern, modules are defined using the define() function. The define() function takes two parameters, the first being an array of dependencies required by the module, and the second being a callback function that returns the module. Here's a basic example:
define(['module1', 'module2'], (module1, module2) => { // Module definition and logic here return { // Module export object };});Here, the define() function is used to define a module that depends on two other modules: module1 and module2. The module's logic is defined inside the callback function, and the module export object is returned. To load the defined module, you would use a separate script loader library such as RequireJS, here's an example of how to load the above module using RequireJS:
require(['mymodule'], (mymodule) => { // Access module methods and properties here});In the example above, the require() function is used to load the myModule module. The module's methods and properties can then be accessed inside the callback function.
Advantages in Brief
One of the advantages of using AMD is that it enables you to load dependencies only as and when they are needed, improving the loading time of your application. AMD also provides a clear structure for organising and managing modules in large web applications, where codebases can otherwise become unwieldy and difficult to work with.
Disadvantages in Brief
However, there are some potential issues to consider when using AMD. One issue is that managing module dependencies can become complex, particularly in larger web applications with many modules. This can also increase the complexity of the overall codebase, which can make it harder to maintain and debug.
I realise the irony in saying that, given what I've just said in the section above. Anything designed to simplify a codebase also has the potential to make it worse too without strict usage guidelines.
Another common problem when using AMD is managing dependencies between modules. As the number of modules and dependencies grows, it can become difficult to keep track of them all. One solution to this problem is to use a package manager such as npm or Yarn to manage dependencies.
Despite this, AMD remains a popular choice for defining and loading modules in web applications. Its simplicity and ease of use make it an attractive option for many web developers.
A More Advanced Example
Now let's take a look at a more advanced example of how AMD can be used in a real‑world scenario. In this example, we will define a module that uses jQuery and Underscore.js as dependencies:
define(['jquery', 'underscore'], ($, _) => { // Module definition and logic here const myModule = { // Module properties and methods init: function () { // Initialise module here }, doSomething: function () { // Module logic here }, }; return myModule;});In the example above, we are defining a module that depends on jQuery and Underscore.js. The module's logic is defined inside an object named myModule, which contains properties and methods that can be accessed by other parts of the application.
To load the module, we can use the require() function from RequireJS like this:
require(['mymodule'], (mymodule) => { // Initialise module mymodule.init(); // Call module method mymodule.doSomething();});Here we're initialising the module and calling its doSomething() method. The module's methods and properties are accessible via the mymodule object.
Alternatives
There are two main alternatives available to us as developers, although one isn't anywhere near production‑ready just yet...
CommonJS
CommonJS is another module system used in JavaScript. However, CommonJS modules are synchronous, meaning that they are loaded at run‑time (before the application runs). This can lead to slower application startup times, but it can also simplify the codebase and make it easier to manage dependencies.
ES6 Modules
When ES6 is released and widely adopted, we will also have access to ES6 modules. Obviously, specifications can (and probably will) change, but at the moment it looks like modules will provide developers with a built‑in module system using modern JavaScript. They will be similar to AMD modules but will be loaded synchronously by default leading to the same performance considerations as CommonJS.
The Wrap‑Up
AMD is a powerful and useful design pattern for defining and loading modules in JavaScript. By using AMD, you can improve the performance and maintainability of your web applications, whilst also simplifying the code structure. As with most things, there are some potential issues to consider but nevertheless, AMD remains a popular choice for many web developers due to its simplicity and ease of use.
Categories:
Related Articles

Getting Started with Callbacks in JavaScript. 
What are Higher‑Order Components in React? What are Higher‑Order Components in React?

JavaScript Essentials for Freelance Web Developers. JavaScript Essentials for Freelance Web Developers

LeetCode: Converting Roman Numerals to Integers. LeetCode: Converting Roman Numerals to Integers

What is Front‑End Development? What is Front‑End Development?

Solving the 'Letter Combinations of a Phone Number' Problem with TypeScript. Solving the 'Letter Combinations of a Phone Number' Problem with TypeScript

How to Use and Clear the CSS float Property. How to Use and Clear the CSS
floatProperty
Object Property Shorthand and Computed Property Names in JavaScript. Object Property Shorthand and Computed Property Names in JavaScript

Simplify Your Layout CSS with place‑items. Simplify Your Layout CSS with
place‑items
Some of the Most‑Misunderstood Properties in CSS. Some of the Most‑Misunderstood Properties in CSS

Validating Parentheses Input Using TypeScript. Validating Parentheses Input Using TypeScript

Angular Change Detection: How It Works and How to Optimise It. Angular Change Detection: How It Works and How to Optimise It