Node.js - 异步模块加载

2024-03-16

是否可以异步加载 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不支持?


While require是同步的,Node.js 不提供开箱即用的异步变体,您可以轻松地自己构建一个。

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

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

module.exports = passwords;

您可以像往常一样使用此模块:

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

现在,您要做的就是将其变成异步模块。正如你不能delaymodule.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 () { ... });

可能看起来很混乱。因此它may更好(尽管这取决于您的实际场景)使用异步导出对象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 或任何其他异步机制,这使您的语法看起来更好,但最终,它(内部)总是归结为我刚刚在这里描述的模式。基本上,承诺和合作。只不过是回调的语法糖而已。

一旦你像这样构建了模块,你甚至可以构建一个requireAsync功能就像您最初在问题中建议的那样工作。您所要做的就是坚持初始化函数的名称,例如initialize。然后你可以这样做:

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

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

请注意,当然,loading由于限制,该模块仍将同步require函数,但其​​余的都将按照您的意愿异步进行。

最后一点:如果你想真正做到loading模块异步,你could实现一个函数,使用fs.readFile异步加载文件,然后通过eval调用实际执行模块,但我会highly建议反对这一点:一方面,你失去了所有的便利功能request例如缓存等,另一方面,你必须处理eval- 众所周知,评估是邪恶的。所以不要这样做。

尽管如此,如果你still想要这样做,基本上它的工作原理是这样的:

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…
  });
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Node.js - 异步模块加载 的相关文章

随机推荐

  • Java 6 的 WatchService [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Java 7 推出WatchService用于连续监控文件系统 Java 6 有向后移植吗 是否有具有
  • JUnit 测试:通过模拟抑制枚举构造函数?

    我知道可以模拟单个枚举 使用如何使用 Mockito Powermock 模拟枚举单例类 https stackoverflow com questions 15939023 how to mock an enum singleton cl
  • 如何在 AUTO_ACKNOWLEDGE JMS 会话场景中模拟消息重新传递?

    在下面的测试中 我尝试模拟以下场景 消息队列启动 启动设计为在消息处理期间失败的消费者 产生一条消息 消费者开始处理消息 在处理过程中抛出异常来模拟消息处理失败 失败的消费者被停止 另一个消费者启动的目的是接收重新传递的消息 但我的测试失败
  • SSL:尝试使用 Google Assistant SDK 时的 CERTIFICATE_VERIFY_FAILED

    当尝试从我的 Raspberry Pi 授权访问 Google Assistant API 时 我收到 SSLError 类型的错误 指出证书验证失败 当我跑步时 python m googlesamples assistant auth
  • 获取数字并输出其英文单词的算法

    我想用 C 语言编写一个程序 要求用户输入一个数字 然后用英文打印该数字 例如 if INPUT 1 then print ONE if INPUT 2 then print TWO 等等 它可以使用 switch case 和 if el
  • 移动设备上的 Javascript 点击事件

    我有一个 Javascript 函数 在 PC 上运行良好 但在移动设备上加载时不起作用 我认为这是因为移动设备对待点击的方式不同 但是如何修改以下功能以使其在移动设备上运行 function var a 48 0 49 1 50 2 51
  • DRF 序列化器中多个查找字段的自定义超链接 URL 字段

    我在用Django 休息框架 http www django rest framework org 用于为我的项目开发 Web api 在我的项目中 我需要构建嵌套 api 的端点 如下所示 users to get all users u
  • DICOM 图像中引用的图像序列中的[引用的 SOP 类/实例 UID] 是什么?

    我正在使用 fo dicom 库开发模态工作列表客户端 我不清楚以下与 有关的事情Referenced SOP Instance UID 0008 1155 什么是引用的 SOP 实例 UID 整个系列的参考 SOP 实例 UID 是否相同
  • 将密码迁移到 Devise

    我正在将用户数据库从 PHP 迁移到 Rails 我已经安装了 Devise Gem 现在运行良好 另外 我还发现了如何将现有用户的密码迁移到 Rails 的提示 我已将旧密码添加到相同的密码中encrypted passwordDevis
  • Android Marshmallow 6.0.1 蓝牙扫描未返回结果

    根据 Kitkat 4 4 4 的 update appCompat 使用以下代码和权限 6 01 中的蓝牙似乎无法按预期工作 没有返回任何结果 并且我附近有几个可发现的设备 有人对为什么有任何见解吗 我在 Nexus 5 上运行
  • python -正则表达式匹配单词列表

    我有一个 python 脚本 其中大约有 100 个正则表达式行 每行匹配某些单词 显然 该脚本每次运行时都会消耗高达 100 的 cpu 我基本上向它传递了一个句子 它会返回找到的任何匹配的单词 我想将它们组合成大约 4 或 5 个不同的
  • PHPUnit - getallheaders 不起作用

    我正在测试我的代码 并且标头有一些问题 在我使用的每个 api 中 headers getallheaders 为了实现这一点 当我使用应用程序或 chrome postman 扩展进行测试时 效果很好 当我启动测试时 像这样 client
  • 动态设置onclick并传入元素本身来访问innerHTML

    我正在动态创建一些 div 元素 然后填充它们innerHTML带有文本的属性 我正在尝试设置他们onclick事件处理程序如下 myDiv onclick function alert Hello 我能做到的 我想做的是能够访问新定义的值
  • 外键约束失败

    我在 php 和 mysql 方面相对较新 在我的值中插入值时我面临的问题leave表 我的leave包含以下列的表 1 lid INT主键 2 empname varchar 3 用户名 varchar 4 点头 INT 5 sdate
  • 使用设备构建时,Monotouch 在 LINQ 查询上崩溃

    这是我得到的错误 mscorlib 在使用 aot only 运行时尝试 JIT 编译方法 System Linq OrderedEnumerable 1 GetEnumerator 从我读到的内容看来 编译器在本例中不包含 GetEnum
  • 带有 CSV 文件的 azure Terraform 参数

    我正在尝试使用 CSV 文件访问 terraform 变量数据 创建资源组并将资源组的名称添加到 CSV 文件中并尝试访问代码 这是代码 locals Resource groupname csvdecode file path modul
  • 如何将垂直线的表格图像分成三张图像?

    我想将垂直线上的表格图像分成三个图像 如下所示 是否可以 每列的宽度是可变的 可悲的是 如您所见 左侧垂直线是从标题向下绘制的 输入图像 input png 输出图像 output1 png 输出图像 output2 png 输出图像 ou
  • 如何学习阿格达

    我正在努力学习agda 但是 我遇到了一个问题 我在 agda wiki 上找到的所有教程对我来说都太复杂了 并且涵盖了编程的不同方面 在并行阅读了 3 个关于 agda 的教程后 我能够编写简单的证明 但我仍然没有足够的知识来使用它来实现
  • 调用随机函数 Javascript,但不能调用同一函数两次

    我使用一个随机选择另一个有效函数的函数 但有时它会连续运行相同的函数两次甚至更频繁 有办法防止这种情况吗 我当前的代码 window setInterval function var arr func1 func2 func3 rand M
  • Node.js - 异步模块加载

    是否可以异步加载 Node js 模块 这是标准代码 var foo require foo js waiting for I O foo bar 但我想写这样的东西 require foo js function foo foo bar