Unloading lazy getters in boostrap addons? - javascript

At the top of my bootstrap.js I define a bunch of lazyGetters, instead of a JSM:
const myServices = {};
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyGetter(myServices, 'sss', function(){ return Cc['#mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService) });
I heard that you have to unload imported modules. But what about "modules" that you create for lazyGetters? How would I unload those? Would I do a delete myServices?
If I do a delete on myServices which is a global, does that mean I should delete all my global variables on unload?
I read here: Forgetting to unload JavaScript modules in restartless add-ons
Forgetting to unload JavaScript modules in restartless add-ons
Another common cause of leaks is forgetting to unload JavaScript code
modules in bootstrapped add-ons. These leaks cannot be detected by
looking at about:compartments or about:memory because such modules
live within the main System compartment.
Also, when your add-on gets updated and re-enabled, the previous
module version that is still loaded will be used, which might break
your add-on entirely.
The following example shows how to unload your modules again
(bootstrap.js):
Components.utils.import("resource://gre/modules/Services.jsm");
function startup(data, reason) {
// This assumes your add-on did register some chrome
Components.utils.import("chrome://myaddon/content/mymodule.jsm");
}
function shutdown(data, reason) {
if (reason != APP_SHUTDOWN) {
// No need to do regular clean up when the application is closed
// unless you need to break circular references that might negatively
// impact the shutdown process.
return;
}
// Your add-on needs to unload all modules it ships and imported!
Components.utils.unload("chrome://myaddon/content/mymodule.jsm");
}
Note: Modules not belonging to your add-on — such as Services.jsm — should not be unloaded by your add-on, as this might cause errors and/or performance regressions and will actually increase the memoryusage.

The code you provided does not import a module, but defines a service getter, so it is fine.
(Aside: there is also a XPCOMUtils.defineLazyServiceGetter helper...)
However if you did something like this:
XPCOMUtils.defineLazyGetter(myServices, 'SomeSymbol', function() {
return Cu.import("chrome://myaddon/content/mymodule.jsm", {}).SomeSymbol;
});
then of course you'd need to Cu.unload() that module again. This is best done (IMO) by registering the module to unload as soon as it is loaded, so something like:
XPCOMUtils.defineLazyGetter(myServices, 'SomeSymbol', function() {
let rv = Cu.import("chrome://myaddon/content/mymodule.jsm", {}).SomeSymbol;
unload(function() {
Cu.unload("chrome://myaddon/content/mymodule.jsm");
});
return rv;
});
Other people just pro-actively unload all modules that could have been potentially imported or not. This is fine to, as Cu.unload()ing a module that wasn't imported just does nothing.
PS: You can still run into trouble when sticking something into another module, like.
XPCOMUtils.defineLazyServiceGetter(Services /* other module */,
'sss',
'#mozilla.org/content/style-sheet-service;1',
'nsIStyleSheetService');
In this example XPCOMUtils.jsm might still reference the strings passed as arguments from your code and hence leak your code. (Also, it is rather rude to stick stuff into modules that aren't your own and may create conflicts with other add-ons doing the same thing.)

Related

RequireJS - Loading AMD modules both inside a require flow and inline

Consider this:
<script src='global.js'></script>
<script src='require.js'></script>
<script>
require(['modular_foo'], function() {
//do stuff
});
...and in side global.js we have, among other things:
//global.js
$.getScript("modular_bar.js");
where both modular_foo and modular_bar are anonymously defined AMD modules. Using requireJS, loading something like the above would give you our favourite error, mismatched anonymous define() modules.
It's fine enough as to why that error occurs (read up on that page if you'd like to know), but the problem is, what if you can't get out of this situation?
I'm working in an established platform which is very gradually migrating to a RJS flow, for now there's no way out of using both inline legacy scripts (some of which have AMD checks to trigger define()) and our requireJS entry-point simultaneously.
In some cases I can simply place inline AMD-compatible scripts above loading the require.js library, but that doesn't work when you need to load other things (modular_bar.js) asynchronously depending on the DOM content. I could also just comment out all AMD checks from those files loading externally to RJS but that's preventing making them incompatible with ever being loaded in a modular flow.
Anyone out there had a similar experience? How do you blend your flows to overcome these sorts of conflicts?
I do not have experience using this solution in a production environment but I spent a couple days on this question figuring out the best way to approach it.
You can use a modified RequireJS library that does not allow the anonymous define's to execute, if it is being passed through eval. Also, you can disallow any define calls by removing type check for string on name in the snippet below.
The following snippet is a modification to RequireJS that will ignore anonymous defines if being called by eval. You can find the fully modified require.js in this GitHub Gist.
The code relies on the parse-stack library. If you can't include the library before RequireJS, I suggest just concatenating it to the top of of the file.
Demo
// This is a snippet of RequireJS
// ...
define = function (name, deps, callback) {
var node, context;
// We will allow named modules to be defined by `eval`
if (!(typeof name == 'string' || name instanceof String))
{
var stack = parseStack(new Error());
// If we find any eval in the stack, do not define the module
// This is to avoid the "Mismatched anonymous define() module" error
// Caused by executing an anonymous define without requireJS
for(var i = 0; i < stack.length; i++)
{
if(stack[i].name == "eval")
{
return;
}
}
}
// ...

Require js ruins code navigation

require.js states the way of defining objects inside modules with define([requiremens], object) as best way.
So every page, or other js file, will do require() call and receive modules as parameters.
This works pretty fine, each function/module has own namespace.
The problem is that I have:
// AJAX/Requests.js
define(['UI/Message'],function(Message){
var Requests={
checkResponse:function(response){
//1==ok
//0==error
//2==good message
//3==good message, but still stop
if(response.status==1){
return true;
}
else if(response.status==2){
Message.good(response.message);
return true;
}
else if(response.status==3){
Message.good(response.message);
return false;
}
else{
Message.bad(response.message);
return false;
}
}
};
return Requests;
});
Now the UI/Message is defined in the same way, and it returns object.
But when I edit file with requests, I can't navigate by code, so if I want to edit Message object, the only way is to go and open file the myself and to find function I need, rather than have the IDE jump there for me.
Is there some workaround for pycharm specifically or to require.js in common to solve this issue? When you have a lot of code it becomes a mess to navigate it, which is why I use an IDE in the first place!
And what worse: The editor never knows what functions objects have!
The one possible solution I can see is to not to use enclosed namespaces, and to declare global variable before the define() call, but in this case all objects shall be called like UI_Message, AJAX_Requests. In order to be sure, that I don't have some Message in two different locations....
And I am not sure, if require.js optimizer will use this correctly. Require.js documentation states very clear, to stay away from global variables.
It's a known issue, please star/vote.
From the issue description:
The dojo library switched to AMD's format define() for loading modules
instead of dojo.require(). Previously I was able to use Ctrl+B on
dojo.require('path.to.someJs') to jump to the declaration. This does
not work on the new format define(['path/to/someJs]', ...).
As PyCharm, WebStorm, PhpStorm and IntelliJ IDEA share the same JavaScript plug-in, this issue also applies to the product that you are using. You will continue to observe the described problem until this bug is fixed. Sorry for the inconvenience.
WebStorm (at least 6.0.2) supports code navigation with RequireJs if you're defining your modules with the CommonJs wrapper and use the exports and module arguments:
//foo.js
define(function(require, exports, module) {
//use exports to expose properties for code navigation in other modules
exports.bar = function() {}
});
Apparently, it works even if the module using it doesn't use the CommonJs wrapper format:
define(['./foo'], function(foo) {
foo.bar(); //code navigation works here
}
If the other IDEs use the same JavaScript plug-in as CrazyCoder said, it may work on their newer releases as well.

Curl throwing ‘Promise already completed’ error

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.

Patterns or techniques for ensuring script availability?

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.

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