Patterns or techniques for ensuring script availability? - javascript

As a project I have been working on has grown, so has the frequency of situations where all scripts on a page are not available when other code tries to access them. Though this happens most often after code is updated (e.g. not cached) I've had it come up more and more in testing, when it never used to happen.
I've addressed this, partially, by using a function to wait for a module to become available (see this question) and this addresses the concern, mostly, but I'm not totally thrilled with the implementation, and am looking for a more industrial strength pattern to deal with this. These are possible solutions I've come up with:
1) Load scripts on demand with something like ensure - not ideal. Requires actual script name dependency information to be included in each script, not just module/object name, to do this. Still have to take some action before using a resource to ensure it's available.
2) Manage script loading order. If this would even work (e.g. I don't think that simply putting script A before script B guarantees it will be available since they can be loaded concurrently), it would be a pain, since you don't know a dependency until you've loaded the thing that depends on it. Would require a lot of work to set up on a site that has lots of pages that use different resources (and I have no intention of loading everything used everywhere on the site on every page).
3) Wait for everything to be loaded on a given page before allowing user interaction. Far from ideal for obvious reasons. Still doesn't address dependencies that happen in initialization code.
4) Expand upon my current solution. Currently works like (this is pseudocode, but the basic logic process):
// Depends on Module2
Module1 = (function () {
self = {};
// function requires dependency
// waitFor waits until global named 'dependency' is available then calls callback
self.initialized=false;
self.init = function() {
waitFor('Module2', function() {
self.intialized=true;
});
}
// waitForInitialization sets a callback when self.initialized=true
self.func = self.waitForInitialization(func() {
Module2.doStuff();
});
}
//UI-initiated function requires dependency
self.uiFunc = function() {
if (!self.initialized) {
showPleaseWaitDialog();
self.waitForInitialization(function() {
dismissPleaseWaitDialog();
self.uiFuncImpl);
} else {
self.uiFuncImpl();
}
}
self.uiFuncImpl= function() {
Module2.doStuff();
}
} ());
I can think of ways to create a prototype that would deal with the dependency issue more transparently than my code above, and fully intend to do that if I have to, but is this truly the best solution? What do others do? What are considered best practices?

2) Script Load Order - scripts will always be executed in the order they are placed in the DOM, so while they might load concurrently they will execute in an orderly fashion (faced this same problem on a large project I worked on).
?) If script load order is not an ideal solution for you, you could look into the Promise model.
??) If Promises and Load Order won't work for you, you could listen for a namespaced event that each module could fire when it's initialized, that way if the object exists it can be used and if not its initialization could be listened for.

Related

Reload module at runtime

I am considering to port a highly demanded(lots of traffic) sockets-based architecture from .NET to Node.JS using Socket.IO.
My current system is developed in .NET and use some scripting languages, loaded at runtime, so I can do hot-fixes if needed by issuing a reload command to the server, without having to restart the different servers/dispatcher processes.
I originally built it this way so, like I said, I could do hot fixes if needed and also keep the system available with transparent fixes.
I am new to Node.JS but this is what I want to accomplish:
Load javascript files on demand at runtime, store them in variables somewhere and call the script functions.
What would be the best solution? How to call a specific function from a javascript file loaded at runtime as a string? Can i load a javascript file, store it in a variable and call functions in a normal way just like a require?
Thanks!
If I understood your question correctly. You can check the vm module out.
Or if you want to be able to reload required files, you must clear the cache and reload the file, something this package can do. Check the code, you'll get the idea.
Modules are cached after the first time they are loaded. This means
(among other things) that every call to require('foo') will get
exactly the same object returned, if it would resolve to the same
file.
Multiple calls to require('foo') may not cause the module code to be
executed multiple times. This is an important feature. With it,
"partially done" objects can be returned, thus allowing transitive
dependencies to be loaded even when they would cause cycles.
More information can be found here.
Delete the cached module:
delete require.cache[require.resolve('./mymodule.js')]
Require it again. (maybe a require inside a function you can call)
update
Someone I know is implementing a similar approach. You can find the code here.
You can have a look at my module-invalidate module that allows you to invalidate a required module. The module will then be automatically reloaded on further access.
Example:
module ./myModule.js
module.invalidable = true;
var count = 0;
exports.count = function() {
return count++;
}
main module ./index.js
require('module-invalidate');
var myModule = require('./myModule.js');
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
mySystem.on('hot-reload-requested', function() {
module.invalidateByPath('./myModule.js'); // or module.constructor.invalidateByExports(myModule);
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
});

When does the browser's event loop start?

I'm using a framework which features auto-connecting to server on page load. I can disable it by passing options arguments, but the line that confuses me is this:
You can prevent this initial socket from connecting automatically by disabling io.sails.autoConnect before the first cycle of the event loop elapses.
My questions are:
When does the first cycle of the event loop elapses?
Is this behaviour the same across ALL modern (IE9+) browsers?
I have a bunch of scripts (in <body>) loading between the lib and my entry file. Does this affect when the first cycle elapses? EDIT: Yes, it does.
How can I ensure my code runs before the first cycle elapses?
Is this kind of implementation of auto-connect considered good practice?
The documentation for the source file is a little more explicit; it says "This can be disabled or configured by setting io.socket.options within the first cycle of the event loop."
Basically what's happening is that there exists within the library a setTimeout(fn, 0) call, which is idiomatic for starting a parallel process. However, in the JS standards it's explicitly stated that JS is single-threaded: in other words, even though setTimeout and setInterval are asynchronous they are not actually parallel in the sense that any of their code will be executing simultaneously with any other code. So they wait until the current function is over before they execute. This queueing mechanism is known as the JavaScript event loop.
I believe that what you are asked to do by the script author is to modify the source to include the relevant change, perhaps at the bottom of the file for your convenience.
It is also likely that a similar effect will be achieved by putting a <script> tag underneath the one that loads the given JS. This has not been explicitly standardized by HTML 4, but may be implicitly standardized in the new HTML 5 spec (it's a complicated interaction between different parts of the specs).
In terms of HTML5, it looks like the current specs say that there is a afterscriptexecute event and a load event which occur immediately after any remote script is loaded (or, if it's an inline script, the load event is scheduled as a task -- I am not sure when those occur). So you might be able to guarantee it without modifying the script by instead doing:
<script>
function do_not_autoload() { /* ... */ }
</script>
<script onload="do_not_autoload()" src="./path/to/sails.io.js"></script>
but I'm not sure what the compatibility table for script#onload is going to look like.
I made you a jsfiddle which can be used to grab a 'fingerprint' for different browsers to get an idea of what evaluation orders are out there in the wild. The * is the document.body.onload event. On my system it produces:
Firefox 32.0.3 : cafdbe*
Chrome 37.0.2062 : cafd*be
IE 11.0.9600 : cafd*be
In other words,

Is it possible to make an object created in console persistent throughout the session?

Goal: making a standalone modular JavaScript debuggin-utility (includes custom dom and event manipulation methods) to be used in the console (preferably Chrome) on any random sites of interests (with no backend access).
Usage: initially include module script directly via copy-paste to console or by creating a new script element pointing at myhomepage.com/shortandeasytoremember.js and call methods on the namespace from there on.
Problem: how to best make it persistent throughout the session on that webpage (so that I wouldn't need to reinclude it after every refresh) ?
Note: any additional browser compatibility is not required - as long as it works in the latest Chrome, it's all fine by me (but any effort in the compatibility department is always much appreciated for the sake of others). IF YOU READ THIS IN A FAR FUTURE and by then there exists a better solution than what is written down below, please take a moment to contribute with your superior knowledge.
What I currently have is an event listener on window.unload to save any state data to localStorage and a string to make it easier to reinclude the module after page reload using eval(localStorage.getItem('loadMyNS'));.
(function(ns, undefined){
'use strict';
//util methods on ns and few monkey patches for debugging ...
var store = 'if(!window.MyNS){' +
'var xyz9=document.createElement("script");' +
'xyz9.src="http://myhomepage.com/shortandeasytoremember.js";' +
'document.head.appendChild(xyz9);}';
localStorage.setItem('loadMyNS', store);
ns.save = function () {
// and use localStorage for some more data
// to be used by other methods after page reload
};
window.addEventListener('unload', ns.save, false);
}(window.MyNS = window.MyNS || {}));
(browsers with no localStorage or addEventListener may benifit from this article)
I've also concidered using the same schema with window.name instead of localStorage (as long as this still seems legid) just because writing eval(window.name) would take less typing ^^.
The trouble (one of them) I have with the "eval-script-tag-inclusion" is on the sites which block external non-https script sources. An ideal solution would be a globally accessible module which would live with state and methods included (and no initialization required after refresh) at least until I close the the window (or overwrite the ref ofc).
If that is currently absolutely not possible, a lesser solution yet still superior to my current code would suffice.

$(window).load fires too quickly in IE with asynchronous js libs

I have some serious problem with getting asynchronously some js libs and executing them in $(window).load in IE
all works in other browsers of course
so the problem is, that I'm doing something like
<script type="text/javascript">
var scr1 = document.createElement('script');
scr1.type = 'text/javascript';
scr1.src = 'some_lib.js';
$('BODY').prepend(scr1);
</script>
Just before </body> and use $(window).load method in html above it to operate on some plugins in some_lib.js, but it all happens to fast in IE, probable because of that asynchronous lib including, and I get an error, that method is not available for the element.
Is there any chance of maybe modyfying $(window).load method so I still could use it in the same way for every browser ?
Any code that you have in the window.load() call must be placed in a function (called onLoad in this example).
Every time you have a script that you dynamically load, increment a counter. Also include something to decrement that counter...
src1.onload = function() { counter--; onLoad(); }
Then in 'onLoad' have the first line...
if (counter > 0) return;
That means that onLoad will fire at window.load and after every script is loaded, but will only execute when it's all loaded.
It's scrappy, but it will solve your problem.
You haven't really described the reason you need to load these libraries asynchronously. Third party libraries often have "on script load" functionality that you can define before the script is loaded. If you need to load multiple libraries before you can execute your code, you may have to either 1. fire up some code every time a library is loaded to test to see if all libraries required are loaded and then fire off you code 2. for every library, create a jQuery promise/deferred to get resolved when that library is loaded and use $.when(promises).done(function/code) to test and run the code whenever a particular set is loaded, or 3. rewrite to use RequireJS. If these libraries are YOUR code, well, you may have to add start up code to your libraries anyway; It might be a good time to learn RequireJS.
I wish I could recommend further, but learning the basics behind RequireJS has always been on my todo list, but it hasn't been done; I just know of people here successfully using it. If that seems like too much trouble, I'd consider some variant of option 2. If you don't know what jQuery would be used eh... you may be stuck with option 1 or 3.
Edit:
Of course, that's not to say that jQuery has got the only promise library, I just often recommend using promises in some form for these kind of things..
Archer's technique looks interesting, I just don't know how reliable it is (it might be quite reliable, I just would like to see proof/documentation). You could combine that with option 2 also, quite well, if you want to short-cut execution for some things while leaving others to be dealt asynchronously and if those script onload methods really work as expected.

Is it possible to use requirejs when modules may have to be removed to conserve memory

We develop an application in an embedded environment. It is a high level computing environment with a complete webbrowser on top of a busybox Linux system. The only exception is that the system has a limited amount of system memory.
Our application is built in JavaScript and runs inside a Webkit based webbrowser and consists of a lot of javascript modules that are loaded in sequence (Which is not very efficient).
Some modules provide common functionality that is used by several modules. We are in the process of converting our current javascript loader with requirejs, but there is one specific need we have to address first.
Is it possible to unload a module when it has been loaded using requirejs? Assume that we dynamically loads a module using :
require(["somemodule.js"], function(m) { m.run(); } );
That works well for loading and running 'somemodule' and also resolving all dependencies for 'somemodule' and the requirejs framework will store a reference to 'somemodule' for future requests.
If we at some point need to reclaim memory, e.g to be able to load and run an infinite number of modules, we have to start removing some of them after some time. Is that possible with requirejs without altering the internal implementation?
Has anyone dealt with this kind of problem before? Most single page JS apps runs in a webbrowser on a desktop PC where memory usage usually is not a major concern.
RequireJS does not have a built-in unload feature, but it could be added perhaps as an additional part you could build into it. If you would like to have that feature, feel free to propose it in the mailing list or as a GitHub issue.
If you wanted to experiment to see if it helps your situation, what you need to do is the following:
1) Remove the defined module from the RequireJS module cache. If you are not using the multiversion support, you can do something like:
var context = require.s.contexts['_'];
delete context.defined[moduleName];
delete context.specified[moduleName];
delete context.loaded[moduleName];
2) Then you can try removing the script tag to see if that helps:
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length - 1; i >= 0; i--) {
var script = scripts[i];
if (script.getAttribute('data-requiremodule') === moduleName) {
script.parentNode.removeChild(script);
break;
}
}
Note that the module may not be garbage collected if another module holds on to it via the closure function(){} that defines that other module. That other module would need to be removed too.
You can try to limit that impact by not passing in the module as a function argument, but just use require("somemodule") inside the function definition whenever you want to get a hold of dependent modules, and not holding on to that require return value for too long.
Also, in your example above, for modules that use require.def to define themselves, it should look like this (without the .js suffix):
require(["somemodule"], function(m) { m.run(); } );
Try this: require.undef(moduleName)

Categories