Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to unload, delete, or unrequire a Nodejs module

We are building an Electron app that allows users to supply their own 'modules' to run. We are looking for a way to require the modules but then delete or kill the modules if need be. We have looked a few tutorials that seem to discuss this topic but we can't seem to get the modules to fully terminate. We explored this by using timers inside the modules and can observe the timers still running even after the module reference is deleted.

https://repl.it/repls/QuerulousSorrowfulQuery

index.js

// Load module
let Mod = require('./mod.js'); 

// Call the module function (which starts a setInterval)
Mod();

// Delete the module after 3 seconds
setTimeout(function () {
  Mod = null;
  delete Mod;
  console.log('Deleted!')
}, 3000);

./mod.js

function Mod() {
  setInterval(function () {
    console.log('Mod log');
  }, 1000);
}

module.exports = Mod;

Expected output

Mod log
Mod log
Deleted!

Actual output

Mod log
Mod log
Deleted!
Mod log 
...
(continues to log 'Mod log' indefinitely)

Maybe we are overthinking it and maybe the modules won't be memory hogs, but the modules we load will have very intensive workloads and having the ability to stop them at will seems important.

Edit with real use-case

This is how we are currently using this technique. The two issues are loading the module in the proper fashion and unloading the module after it is done.

renderer.js (runs in a browser context with access to document, etc)

const webview = document.getElementById('webview'); // A webview object essentially gives us control over a webpage similar to how one can control an iframe in a regular browser.
const url = 'https://ourserver.com/module.js';
let mod;
request({
  method: 'get',
  url: url,
}, function (err, httpResponse, body) {
  if (!err) {
    mod = requireFromString(body, url); // Module is loaded
    mod(webview); // Module is run
    // ...
    // Some time later, the module needs to be 'unloaded'. 
    // We are currently 'unloading' it by dereferencing the 'mod' variable, but as mentioned above, this doesn't really work. So we would like to have a way to wipe the module and timers and etc and free up any memory or resources it was using!
    mod = null;
    delete mod;
  } 
})

function requireFromString(src, filename) {
  var Module = module.constructor;
  var m = new Module();
  m._compile(src, filename);
  return m.exports;
}

https://ourserver.com/module.js

// This code module will only have access to node modules that are packaged with our app but that is OK for now!
let _ = require('lodash'); 
let obj = {
  key: 'value'
}
async function main(webview) {
  console.log(_.get(obj, 'key')) // prints 'value'
  webview.loadURL('https://google.com') // loads Google in the web browser
}

module.exports = main;

Just in case anyone reading is not familiar with Electron, the renderer.js has access to 'webview' elements which are almost identical to iframes. This is why passing it to the 'module.js' will allow the module to access manipulate the webpage such as change URL, click buttons on that webpage, etc.

like image 451
T Mack Avatar asked Sep 11 '25 19:09

T Mack


1 Answers

There is no way to kill a module and stop or close any resources that it is using. That's just not a feature of node.js. Such a module could have timers, open files, open sockets, running servers, etc... In addition node.js does not provide a means of "unloading" code that was once loaded.

You can remove a module from the module cache, but that doesn't affect the existing, already loaded code or its resources.

The only foolproof way I know of would be to load the user's module in a separate node.js app loaded as a child process and then you can exit that process or kill that process and then the OS will reclaim any resources it was using and unload everything from memory. This child process scheme also has the advantage that the user's code is more isolated from your main server code. You could even further isolate it by running this other process in a VM if you wanted to.

like image 198
jfriend00 Avatar answered Sep 14 '25 08:09

jfriend00