当前位置: 首页 > 面试题库 >

Node.js-异步模块加载

邴越彬
2023-03-14
问题内容

是否可以异步加载Node.js模块?

这是标准代码:

var foo = require("./foo.js"); // waiting for I/O
foo.bar();

但是我想写这样的东西:

require("./foo.js", function(foo) {
    foo.bar();
});
// doing something else while the hard drive is crunching...

有没有办法做到这一点?还是有一个很好的理由为什么require不支持回调?


问题答案:

尽管require是同步的,并且Node.js并未提供现成的异步变体,但是您可以轻松地自己构建一个变体。

首先,您需要创建一个模块。在我的示例中,我将编写一个模块,该模块从文件系统异步加载数据,但是当然是YMMV。因此,首先,使用老式的,不需要的同步方法:

var fs = require('fs');
var passwords = fs.readFileSync('/etc/passwd');

module.exports = passwords;

您可以照常使用此模块:

var passwords = require('./passwords');

现在,您要做的就是将其变成一个异步模块。由于您不能 延迟
module.exports,因此您要做的是立即导出一个异步执行工作的函数,并在完成后立即回叫您。因此,您将模块转换为:

var fs = require('fs');
module.exports = function (callback) {
  fs.readFile('/etc/passwd', function (err, data) {
    callback(err, data);
  });
};

当然,您可以通过直接将callback变量提供给readFile调用来缩短此时间,但是出于演示目的,我想在此处将其明确显示。

现在,当您需要此模块时,起初没有任何反应,因为您仅获得对异步(匿名)功能的引用。您需要立即调用它并提供另一个函数作为回调:

require('./passwords')(function (err, passwords) {
  // This code runs once the passwords have been loaded.
});

当然,使用这种方法,您可以将任何任意的同步模块初始化都转换为异步初始化。但是窍门总是一样的:导出一个函数,从require调用中直接调用它,并提供一个回调,一旦异步代码运行,该回调将继续执行。

请注意,对于某些人

require('...')(function () { ... });

可能会令人困惑。因此,使用异步函数或类似的方法导出对象 可能 会更好(尽管这取决于您的实际情况)initialize

var fs = require('fs');
module.exports = {
  initialize: function (callback) {
    fs.readFile('/etc/passwd', function (err, data) {
      callback(err, data);
    });
  }
};

然后,您可以使用

require('./passwords').initialize(function (err, passwords) {
  // ...
});

可能可读性更好。

当然,您也可以使用Promise或任何其他异步机制,这会使您的语法看起来更好,但最后,它(内部)总是归结为我在此描述的模式。基本上,promise&co。只是回调上的语法糖。

一旦构建了这样的模块,您甚至可以构建一个requireAsync功能,该功能的运行与您最初在问题中建议的一样。您所要做的就是坚持使用初始化函数的名称,例如initialize。然后,您可以执行以下操作:

var requireAsync = function (module, callback) {
  require(module).initialize(callback);
};

requireAsync('./passwords', function (err, passwords) {
  // ...
});

请注意,由于功能的限制,当然, 加载 模块仍将是同步的require,但是其余所有模块将根据您的需要是异步的。

最后一点:如果要使 加载 模块真正异步, 可以
实现一个函数,该功能fs.readFile用于异步加载文件,然后通过eval调用运行该文件以实际执行该模块,但是我 强烈
建议您这样做:一方面,您失去了request诸如caching&co。之类的所有便利功能,另一方面,您将不得不处理eval-众所周知,
eval是邪恶的 。所以不要这样做。

不过,如果您 仍然 想要这样做,则基本上它的工作原理如下:

var requireAsync = function (module, callback) {
  fs.readFile(module, { encoding: 'utf8' }, function (err, data) {
    var module = {
      exports: {}
    };
    var code = '(function (module) {' + data + '})(module)';
    eval(code);
    callback(null, module);
  });
};

请注意,此代码不是“不错的”代码,它没有任何错误处理功能,也没有原始require功能的任何其他功能,但是基本上,它满足了您能够异步加载同步设计的模块的需求。

无论如何,您可以将此功能与以下模块一起使用

module.exports = 'foo';

并使用以下命令加载它:

requireAsync('./foo.js', function (err, module) {
  console.log(module.exports); // => 'foo'
});

当然,您也可以导出其他任何内容。也许是为了与原始require功能兼容,可能会更好运行

callback(null, module.exports);

作为requireAsync函数的最后一行,那么您就可以直接访问exports对象(foo在这种情况下为字符串)。由于您将加载的代码包装在立即执行的函数中,因此该模块中的所有内容均保持私有状态,并且与外界唯一的接口就是module您传入的对象。

当然,可以辩称这种用法evil不是世界上最好的主意,因为它会打开安全漏洞,等等。但是,如果您使用require一个模块,则基本上除了eval对它进行评估外,别无所求。关键是:如果您不信任代码,eval那么与的想法是一样的require。因此,在这种特殊情况下,可能会很好。

如果使用严格模式,eval对您不利,那么您需要使用该vm模块并使用其runInNewContext功能。然后,解决方案如下所示:

var requireAsync = function (module, callback) {
  fs.readFile(module, { encoding: 'utf8' }, function (err, data) {
    var sandbox = {
      module: {
        exports: {}
      }
    };
    var code = '(function (module) {' + data + '})(module)';
    vm.runInNewContext(code, sandbox);
    callback(null, sandbox.module.exports); // or sandbox.module…
  });
};

希望这可以帮助。



 类似资料:
  • 我试图为我编写的一些代码编写单元测试,遇到的问题是我希望在执行函数后调用模拟回调,但我的测试失败,因为它从未被调用。 正在测试的功能非常简单: 我怀疑的问题是,由于请求的异步性质,jasmine在API调用中执行模拟回调之前测试期望值。我尝试过使用其他人建议使用的done()和flags,但没有成功。希望能在这方面提供一些指导。

  • 问题内容: 我想以异步方式初始化模块,并提出一些想法。我需要具有Mongo集合列表和其他数据的DB对象,但是为了简洁起见,其中的文件列表确实有用。 我无法导出函数或类,因为我每次都需要返回相同的对象。 首先 想到的最简单的方法是分配给它,然后填充它: 不好的事情–我真的从外面不知道什么时候准备好列表,也没有检查错误的好方法。 *我想出的 *第二种 方法是继承并通知所有人数据库已准备就绪或发生了错误

  • 异步模块 if (isIE8) { require.async('compatible-ie8.js', function(exports){ // ... }); } else if (isIE6) { require.async('compatible-ie6.js', function(exports){ // ... });

  • 异步模块 异步模块与同步模块相反,指的是模块是在运行时才去异步加载然后再运行。 setTimeout(function(){ // 异步模块`async.js`在 1000ms 后加载,然后再运行 require.async('async.js', function(exports){ // 模块加载完成后,返回模块的导出 // exports

  • 本文向大家介绍Node.js模块加载详解,包括了Node.js模块加载详解的使用技巧和注意事项,需要的朋友参考一下 JavaScript是世界上使用频率最高的编程语言之一,它是Web世界的通用语言,被所有浏览器所使用。JavaScript的诞生要追溯到Netscape那个时代,它的核心内容被仓促的开发出来,用以对抗Microsoft,参与当时白热化的浏览器大战。由于过早的发布,无可避免的造成了它的

  • 问题内容: 我该如何做这项工作 我试图从异步之一获取同步功能,我需要它来使用FreeTds异步查询作为同步之一 问题答案: 使用deasync-用C ++编写的模块,它将Node.js事件循环暴露给JavaScript。该模块还公开了一个函数,该函数阻止后续代码,但不阻止整个线程,也不引起繁忙的等待。您可以将函数放入循环中: