Ale*_*kii 21 javascript asynchronous node.js
我想以异步方式初始化模块并提出一些想法.我需要带有来自Mongo和其他数据的集合列表的DB对象,但是./为了简洁起见,文件列表也是如此.
我无法导出函数或类,因为我需要require('db')每次都返回相同的对象.
首先也是最简单的东西来到我的脑海里是分配module.exports给Object后来填充它:
var exports = {};
module.exports = exports;
require('fs').readdir('.', function(err, files) {
exports.error = err;
exports.files = files;
});
Run Code Online (Sandbox Code Playgroud)
不好的事情 - 当列表准备就绪并且没有检查错误的好方法时,我真的不知道从外面.
我搞砸的第二种方法是继承EventEmitter并通知每个人DB已准备好或发生错误.如果一切正常 - 继续.
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();
Run Code Online (Sandbox Code Playgroud)
而现在我认为这更合理:
// 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);
});
}
Run Code Online (Sandbox Code Playgroud)
// 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!
});
Run Code Online (Sandbox Code Playgroud)
我应该选择什么?为什么?这个想法总的来说有多糟糕,特别是我的选择?
感谢您的反馈.
zze*_*zen 26
TL; DR:如果您只是计划在启动时读取本地文件,请使用readdirSync()而不是readdir().如果您计划实际从远程数据库读取数据或在运行时执行任何I/O,请使用选项#2 - 回调.下面的解释和代码示例.
详细说明:
虽然起初这看起来像是一个模块/依赖/需求相关的问题,但事实并非如此.这是如何处理异步代码的一般性问题.让我解释:
require()基本上是整个节点中广泛使用的唯一同步函数,它处理I/O(它需要来自文件系统的其他模块).同步意味着它实际上将其数据作为返回值返回,而不是调用回调.
异步编程中最基本的101规则是:
您永远不能使用异步代码并为其创建同步API.
require采用了特殊的同步的版本readFile叫readFileSync.由于模块实际上只是在程序开始时加载,因此它在读取模块时阻止node.js执行的事实不是问题.
但是,在您的示例中,您尝试执行其他异步I/O - readdir()在require阶段完成.因此,您需要使用此命令的同步版本或API需要更改...
所以你的问题有背景.
您确定了两个基本选项:
EventEmitter示例基本相同)readdir()命令调用readdirSync()出于简单的原因,我会使用选项#3 - 但前提是您计划在启动时只读取几个文件,如您的示例所示.如果以后您的数据库模块实际上要连接到数据库 - 或者如果您计划在运行时执行任何此操作,请立即跳船并使用异步API.
没有多少人记住这一点,但承诺实际上是如何在node.js中处理异步的原始默认值.在节点0.1.30中,但是删除了 promisses 并用带有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
Run Code Online (Sandbox Code Playgroud)
异步选项:
用于何时使用DB等
// 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
});
Run Code Online (Sandbox Code Playgroud)
一般来说,在模块中有任何状态是非常糟糕的.模块应该公开函数,而不是数据(是的,这需要稍微改变你的代码结构).只需将对数据的引用传递给模块函数作为参数即可.
(编辑:刚才意识到这是你最后一个例子的方法.我对它的投票)
模块1:
module.exports = function(params, callback) { ... }
Run Code Online (Sandbox Code Playgroud)
模块2:
var createSomething = require('module1');
module.exports = function(params, callback) {
...
var db = createSomething(params, function(err, res) {
...
callback(err, res);
}
}
Run Code Online (Sandbox Code Playgroud)
主要代码:
var createSomethingOther = require('module2');
createSomethingOther(err, result) {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)