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

Node.js模块的异步初始化

金阳曜
2023-03-14
问题内容

我想以异步方式初始化模块,并提出一些想法。我需要具有Mongo集合列表和其他数据的DB对象,但是./为了简洁起见,其中的文件列表确实有用。

我无法导出函数或类,因为我require('db')每次都需要返回相同的对象。

首先 想到的最简单的方法是分配module.exportsObject它,然后填充它:

var exports = {};
module.exports = exports;

require('fs').readdir('.', function(err, files) {
  exports.error = err;
  exports.files = files;
});

不好的事情–我真的从外面不知道什么时候准备好列表,也没有检查错误的好方法。

*我想出的 *第二种 方法是继承EventEmitter并通知所有人数据库已准备就绪或发生了错误。如果一切正常-继续前进。

var events = require('events');
var util = require('util');

function Db() {
  events.EventEmitter.call(this);
  this.ready = false;
  this.files = null;
  this.initialize();
}

util.inherits(Db, events.EventEmitter);

Db.prototype.initialize = function() {
  if (this.ready)
    return this.emit('ready');

  var self = this;
  require('fs').readdir('.', function(err, files) {
    if (err)
      return self.emit('error', err);

    self.files = files;
    self.ready = true;
    self.emit('ready');
  });
};

module.exports = new Db();

现在, 我认为这是比较合理的:

// db.js
var exports = {init: init};
module.exports = exports;

function init(callback) {
  callback = (typeof callback === 'function') ? callback : function() {};
  require('fs').readdir('.', function(err, files) {
    delete exports.init;
    exports.result = files; // that's pretty much what I need,
                            // so don't mind result slightly differs
                            // from previous cases
    callback(err);
  });
}



// main.js
var db = require('./db');

// check for `db.init` presence maybe...

db.init(function(err) {
  return err ? console.error('Bad!')
             : console.log(db); // It works!
});

我应该选择什么,为什么?这种想法总体上有多糟糕,尤其是我的选择有多糟糕?

感谢您的反馈。


问题答案:

TL; DR:
如果打算在启动时读取本地文件,请使用readdirSync()代替readdir()。如果您打算实际从远程数据库读取数据或在运行时执行任何I /
O,请使用选项#2-回调。下面的解释和代码示例。

详细说明:

虽然起初这似乎是一个与模块/依赖性/需求相关的问题,但实际上并非如此。这是关于如何处理 异步代码 的通用问题。让我解释:

require()从根本上讲,它是唯一一个在整个节点中广泛使用的,处理I / O的 同步
功能(它需要文件系统中的其他模块)。同步意味着它实际上将其数据作为返回值返回,而不是调用回调。

异步编程中最基本的101条规则是:

永远无法 采用异步代码段并为其创建同步API。

require使用称为的特殊 同步
版本。由于模块实际上仅在程序开始时加载,因此在读取模块时会阻塞node.js执行这一事实不是问题。readFile``readFileSync

在你的榜样然而,试图执行额外的 异步I / O - readdir()实现在需要的阶段。因此,您要么需要使用此命令的 同步
版本,要么需要更改API。

因此,存在您问题的背景。

您确定了两个基本选项:

  1. 使用 承诺 (与EventEmitter示例基本相同)
  2. 使用 回调 (第二个示例很好地说明了这一点),第三个是:
  3. 使用命令的 同步 版本readdir()``readdirSync()

为了简单起见,我将使用 选项#3- 但仅当您打算在启动时仅读取几个文件时(如您的示例所示)。如果稍后您的数据库模块实际上将要连接到数据库-
或如果您打算在运行时执行任何上述操作,请立即开始并使用异步API。

不再有多少人记得这一点,但是promise实际上是在node.js中处理异步的原始默认设置。但是,在节点0.1.30中,删除了许可并由带有function(err, result)签名的标准化回调代替。这样做主要是出于简化的原因。

如今,绝大多数异步调用都将标准回调作为最后一个参数。您的数据库驱动程序可以做到,您的Web框架也可以做到-随处可见。您应该保持流行的设计并使用它。

选择诺言或事件的唯一原因是,如果您可能会发生 多种不同的结果 。例如,可以打开套接字,接收数据,关闭,刷新等。

这不是你的情况。您的模块始终执行相同操作(读取一些文件)。所以 选择#2 是这样(除非您可以保持 同步 )。

最后,以下是两个稍作重写的制胜法宝:

同步选项:
仅在启动时适合本地文件系统

// db.js
var fs = require('fs');
exports = fs.readdirSync('.');

// main.js
var db = require('./db');
// insert rest of your main.js code here

异步选项:
用于何时要使用数据库等。

// db.js
var fs = require('fs'), cached_files;

exports.init = function(callback) {
  if (cached_files) {
    callback(null, cached_files);
  } else {
    fs.readdir('.', function(err, files) {
      if (!err) {
        cached_files = files;
      }
      callback(err, files);
    });
  }
};

// main.js
require('./db').init(function(err, files) {
  // insert rest of your main.js code here
});


 类似资料:
  • 问题内容: 是否可以异步加载Node.js模块? 这是标准代码: 但是我想写这样的东西: 有没有办法做到这一点?还是有一个很好的理由为什么不支持回调? 问题答案: 尽管是同步的,并且Node.js并未提供现成的异步变体,但是您可以轻松地自己构建一个变体。 首先,您需要创建一个模块。在我的示例中,我将编写一个模块,该模块从文件系统异步加载数据,但是当然是YMMV。因此,首先,使用老式的,不需要的同步

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

  • SOFABoot 提供了模块并行启动以及 Spring Bean 异步初始化能力,用于加快应用启动速度。本文介绍如何使用 SOFABoot 异步初始化 Spring Bean 能力以提高应用启动速度。 使用场景 在实际使用 Spring/Spring Boot 开发中,一些 Bean 在初始化过程中执行准备操作,如拉取远程配置、初始化数据源等等。在应用启动期间,这些 Bean 会增加 Spring

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

  • 我在尝试用异步数据初始化过滤器时遇到了麻烦。 过滤器非常简单,它需要将路径转换为名称,但要做到这一点,它需要一个对应数组,我需要从服务器获取该数组。 在返回函数之前,我可以在过滤器定义中做一些事情,但是异步方面阻止了这一点 使用promise可能是可行的,但我不清楚角度负载是如何过滤的。这篇文章解释了如何通过服务实现这样的魔力,但是对于过滤器也可以这样做吗? 如果有人对如何翻译这些路径有更好的想法

  • 问题内容: 我在尝试使用异步数据初始化过滤器时遇到麻烦。 过滤器非常简单,它需要将路径转换为名称,但是这样做需要一个对应数组,我需要从服务器中获取该数组。 在返回函数之前,我可以在过滤器定义中执行操作,但是异步方面阻止了该操作 使用诺言可能是可行的,但我对角负载如何过滤没有明确的了解。这篇文章解释了如何通过服务实现这种魔力,但是是否有可能对过滤器做同样的事情? 而且,如果有人对如何翻译这些路径有更