等待模块导入的异步初始化的最佳方法是什么?

时间:2018-01-30 19:44:41

标签: node.js asynchronous promise synchronous discord

TL; DR:有没有办法在继续在调用模块中执行之前等待具有异步功能的模块导入才能保持模块功能?

我正在开发一个个人节点项目,我已经在模块化/ OOP方式下构建,因为代码库不断增长。一个要求是在模块/对象之间启用日志记录,其中可以在不同时间记录不同的日志文件。我认为我通过创建一个带有init函数的Logger.js文件以一种非常干净的方式解决了这个问题,我只需在我需要的任何模块中导入Logger.js文件就可以随时使用它。以下是用于说明这一点的精简代码:

Logger.js

module.exports.init = function(location) {
    var logFileBaseName = basePath + fullDatePathName;
    var studentLogFile = fs.createWriteStream(logFileBaseName + '-student.log', {flags : 'a'});
    var teacherLogFile = fs.createWriteStream(logFileBaseName + '-teacher.log', {flags : 'a'});

    this.studentLog = function () {
        arguments[0] = '[' + Utils.getFullDate() + '] ' + arguments[0].toString();
        studentLogFile.write(util.format.apply(null, arguments) + '\n');
    }
    this.teacherBookLog = function () {
        arguments[0] = '[' + Utils.getFullDate() + '] ' + arguments[0].toString();
        teacherLogFile.write(util.format.apply(null, arguments) + '\n');
    }
}

这看起来很棒,因为在我的主要入口点我可以做到:

Main.js

const Logger = require('./utils/Logger');

Logger.init(path);
Logger.studentLog('test from Main');

// all my other code and more logging here

在我的其他几十个文件中,我可以做得更少:

AnotherFile.js

const Logger = require('./utils/Logger');

Logger.studentLog('test from AnotherFile')

然后,要求不仅要记录“学生日志”的文件,还要记录到Discord(聊天客户端)。看似简单,我有这个Logger文件,我可以初始化Discord并在'学生日志'旁边记录Discord,如下所示:

Logger.js

module.exports.init = function(location) {

    // code we've already seen above

    var client = new Discord.Client();
    client.login('my_login_string');
    channels = client.channels;

    this.studentLog = function () {
        arguments[0] = '[' + Utils.getFullDate() + '] ' + arguments[0].toString();
        var message = util.format.apply(null, arguments) + '\n';
        studentLogFile.write(message);
        channels.get('the_channel_to_log_to').send(message)
    }

    // more code we've already seen above
}

问题是,如果你再次重新运行Main.js,studentLog将失败,因为.login()函数是异步的,它返回一个Promise。在我们尝试拨打Logger.studentLog('test from Main');

时,登录尚未完成且频道将为空集合

我已尝试在Logger.js中使用Promise,但当然,在Logger.js中的promise返回之前,Main.js的执行仍在继续。如果Main.js可以等到Discord登录完成,我会很高兴。

我的问题是,在保持我一直使用的模式的同时,最好的方法是做什么?我知道我可以将我的整个main函数包装在promise.then()中,等待Discord登录完成,但这对我来说似乎有点荒谬。我试图将功能包含在模块中,并且不希望这种Logger代码/逻辑溢出到我的其他模块中。我希望将其保存为一个简单的Logger导入,就像我一直在做的那样。

任何建议都会很棒!!

2 个答案:

答案 0 :(得分:0)

如果等待某个异步函数的结果,然后在同一调用方函数中使用它,则首先解析结果,然后使用。如果结果在另一个函数或模块中使用(例如,结果分配给全局变量),则无法解析。在您的情况下,如果client.login()异步地为client.channels分配了一个值,则该等待未 ,并且channels = client.channels分配会将undefined分配给{ {1}}。

要解决此问题,必须使用回调或从channels返回承诺,如注释中所述。

您可以参考this文章。

答案 1 :(得分:0)

让我提供我对“异步初始化记录器”问题的解决方案。请注意,这仅涉及日志记录,很可能无法推广。

基本上,所有消息都附加到一个队列,该队列仅在指示连接就绪的标志设置后才会发送到远程位置。

示例:

//Logger.js
module.exports = {
  _ready: false,
  _queue: [],

  init(): {
    return connectToRemote().then(()=>{this._ready = true})
  },

  log(message): {
    console.log(message);
    _queue.push(message)
    if (this._ready) {
      let messagesToSend = this._queue;
      this._queue = [];
      this._ready = false;
      sendToRemote(messagesToSend).then(()=>this._ready = true);
    }
  }

}

您可以在任何文件中要求记录器并立即使用日志功能。只有在您可以随时调用的 init 函数解决后才会发送日志。

这是一个非常简单的例子,您可能还想限制队列大小和/或仅在特定时间间隔内批量发送日志,但您明白了。