Node.JS load module async

2019-03-18 15:41发布

问题:

I would like to have the ability to load a module that has been changed. I would have to of course unload the module first. Since this is a webserver setup, I am asking if there is a way to load the module in an async fashion, to avoid freezing the webserver for the duration of the read of the updated file.

Awhile back Node.JS removed the require.async function. So, on the latest version of Node.JS, what would be the recommended alternative?

  • Should I read the entire file first, and then use the Module library to parse the file contents. (as opposed to the default functionality of taking a file name to read and parse internally) How?
  • Should I outsource this job to some open-source library? Which one?
  • Should I write my own module handler - my own implementation of requireAsync? (I know how.)

Note: I do not want to do anything more than load a module async, so please do not recommend that I replace my setup with a new webserver routing framework.

回答1:

I posted this answer, but you are welcome to post an improvement.

See the Node.JS source code.

Module.prototype.require = function(path) { return Module._load(path, this); };

Abridged version of _load

Module._load = function(request, parent, isMain) {
  var filename = Module._resolveFilename(request, parent);
  if (Module._cache[filename]) return Module._cache[filename].exports;
  if (NativeModule.exists(filename)) {
    if (filename == 'repl') { // special case, needs the real require.
      var replModule = new Module('repl');
      replModule._compile(NativeModule.getSource('repl'), 'repl.js');
      NativeModule._cache.repl = replModule;
      return replModule.exports;
    }
    return NativeModule.require(filename);
  }
  var module = new Module(filename, parent);
  if (isMain) process.mainModule = module, module.id = '.';
  Module._cache[filename] = module;
  var hadException = true;
  try {
    module.load(filename);
    hadException = false;
  } finally {
    if (hadException) delete Module._cache[filename];
  }
  return module.exports;
};

My version of require.async.js will be something like

var NativeModule = require('native_module');
var fs = require('fs');
if(!require.async) require.async = function (path, callback) { module.exports(path, this, callback); } // Comment out if you dislike using globals
module.exports = function(request, parent, callback) {
  var filename = Module.resolve(request, parent); // This is a Sync function. TODO, change it to an async function with a callback.
  if (Module.cache[filename]) callback(Module.cache[filename].exports);
  else if (NativeModule.exists(filename)) callback(new Error('What are you thinking?'))
  else fs.readFile(filename, 'utf8', function(err, file) {
    if (Module.cache[filename]) callback(null, Module.cache[filename].exports); // For the case when there are two calls to require.async at a time.
    else if(err) callback(err)
    else {
      var module = new Module(filename, parent);
      try {
        module._compile(file);
        Module.cache[filename] = module;
      } catch(ex) {
        callback(err)
      }
      if(Module.cache[filename]) callback(null, module.exports)
    }
}

Caveats

  • There is one TODO in the code which is to make the multiple calls to stat to be async. The actual reading of the file is regular async, so that is good.
  • If you are async loading a module, and that module you are loading is sync loading another module, then you have not fully gone async with your code - have you.
  • It uses one private method - _compile.