In the current project I am working on, we wanted to load our dependencies in "synchronous" order without the use of a big library like require.js.
We've already used a microframework to load script files, load.js by Chris O'Hara, and we wanted to keep that microframework, as it acts like a "boilerplate" for loading of different script files when we need them.
The result of this lead us to a solution where we integrated a loadDeps function to load our dependencies.
In this how-to, we expect that you already have load.js and jQuery in the DOM.
The dependency loader
The dependency loader looks like this:
javascript
/**
* Load dependencies
*
* Takes three arguments, eventid, scripts_to_load and env_path
*
* scripts_to_load follows this syntax:
*
* <script handle from module> : {
* path : <path to script, relative to environment path>,
* deps : <optional dependency array with handles>,
* onload : <optional function to be fired on load of the script>
* }
*
* NOTE: the dependencies must be an actual script object in
* scripts_to_load, and in the list before the script that requires them!
*
* @param String eventid Most likely the name of the application, without spaces
* @param Object scripts_to_load
* @param String env_path
*/
var loadDeps = function (eventid, scripts_to_load, env_path) {
/**
* Callback for loading of deps
*
* @type Function
* @private
* @param Object script
* @param String handle
* @param String eventid
*/
var _callback = function (script, handle, eventid) {
console.warn(
eventid +
': Script: "' +
handle +
'" loaded from ' +
eventid +
' loader! Please check the build configuration'
);
if (script.onload) {
if (typeof script.onload === 'function') {
script.onload();
}
}
},
_load_dependency = function (env_path, script, handle, eventid) {
load(env_path + script.path).thenRun(function () {
_callback(script, handle, eventid);
});
},
/**
* Is object not in window?
*
* @type Function
* @param String
* @private
* @return Boolean
*/
_is_not_in_window = function (obj) {
return obj in window === false;
},
/**
* Flag for all dependencies loaded
*
* @type Boolean
* @private
*/
_all_dependencies_loaded,
/**
* Flag for all libraries loaded
*
* @type Boolean
* @private
*/
_all_libraries_loaded;
/**
* Loop through scripts_to_load to look for scripts to load
*
* @param Object scripts_to_load
* @key handle Handle of the script to be loaded
* @value script The script package itself
*/
$.each(scripts_to_load, function (handle, script) {
/**
* If the script is not in the window object (not loaded),
* and the script to be loaded got dependencies,
* loop through the dependency list to load the dependencies before
* the main script is loaded
*/
if (_is_not_in_window(handle) && script.deps) {
// Do we have a dependency list with someting in it?
if (script.deps.length) {
/**
* Wait for depencies
*
* @timeout 20
*/
setTimeout(function _waitForDependencies() {
_all_dependencies_loaded = true;
/**
* Loop through script.deps to look for scripts to load
*
* @param Object script.deps
* @key i Index
* @value dependency The dependency handler
*/
$.each(script.deps, function (i, dependency) {
// Is dependency loaded?
if (_is_not_in_window(dependency)) {
// Set to false, since it is not loaded
_all_dependencies_loaded = false;
}
});
// If all dependencies are loaded
if (_all_dependencies_loaded) {
/**
* Load the script that required the dependencies,
* and we load the script and run any required onload methods
*/
_load_dependency(env_path, script, handle, eventid);
} else {
/**
* Wait for depencies interval
*
* @call _waitForDependencies
* @timeout 20
*/
setTimeout(_waitForDependencies, 20);
}
}, 20);
} else {
/**
* If the dependency list is defined, but empty,
* we load the script and run any required onload methods
*/
_load_dependency(env_path, script, handle, eventid);
}
} else if (_is_not_in_window(handle)) {
/**
* If the script is not in the window object (not loaded),
* and have no dependencies (covered in the first conditional statement above),
* we load the script and run any required onload methods
*/
_load_dependency(env_path, script, handle, eventid);
}
});
/**
* Wait for libraries
*
* @timeout 20
*/
setTimeout(function _checkLibraries() {
_all_libraries_loaded = true;
/**
* Loop through scripts_to_load to check if everything is loaded
*
* @param Object scripts_to_load
* @key handle Handle of the script
* @value script The script object
*/
$.each(scripts_to_load, function (handle, script) {
// Is it loaded?
if (_is_not_in_window(handle)) {
_all_libraries_loaded = false;
}
});
// Is everything loaded?
if (_all_libraries_loaded) {
$(document).trigger(eventid + ':dependencies:loaded');
} else {
/**
* Wait for libraries interval
*
* @call _checkLibraries
* @timeout 20
*/
setTimeout(_checkLibraries, 20);
}
}, 20);
};
And it takes three arguments: eventid, scripts_to_load and env_path.
eventid is a String, an ID to be used to identify the event triggered when the dependencies are done loading.
scripts_to_load is an Object containing the dependencies to load, the path of the dependency and an optional callback to run when the dependency is done loading
env_path is a String, a relative path to where the dependencies can be found
The scripts_to_load object
The scripts to load object can look like this:
Missing code!
How to use it
Define your own dependencies as the gist describes it above, then, if you want to, you can define an event listener to listen to the event fired when all the dependencies are loaded:
javascript
$(document).one(
'<ID to be used for the events fired>:dependencies:loaded',
function () {
// Do stuff here when we have finished loading the dependencies
}
);
Then, you pass that object to the window.loadDeps function, with required variables:
javascript
window.loadDeps(
'<ID to be used for the events fired>',
_scripts_to_load,
'<relevant relative path where the script resides>'
);
And that's it! You have now implemented your own JavaScript dependency loader without the use of Require.js!
About the author
Hi! My name is Alexander, and I am a creative frontender, specializing
in UX, accessibility, universal design, frontend-architecture, node and
design systems. I am passionate with open source projects and love to
dabble with new emerging technologies related to frontend. With over
26 years of
frontend experience, I have earned the right to be called a veteran. I
am a lover of life, technologist at heart. If I am not coding, I am
cooking and I love whisky and cigars. Oh, and coffee, I LOVE coffee!
I am also an avid speaker on several topics! Check
out some of the things I speak about,
and contact me if you are interested in having me at your next event!