【问题标题】:Best way to wait for asynchronous initialization from a module import?等待模块导入异步初始化的最佳方法?
【发布时间】:2018-07-09 19:22:34
【问题描述】:

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

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

另一个文件.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 函数包装在等待 Discord 登录完成的 promise.then() 中,但这对我来说似乎有点荒谬。我试图将功能包含在模块中,并且不希望这种 Logger 代码/逻辑溢出到我的其他模块中。我想将它保留为一个简单的 Logger 导入,就像我一直在做的那样。

任何建议都会很棒!

【问题讨论】:

  • 您不想在.then 中将客户端登录包装在logger.js 中吗?即client.login(token).then(success=>{//everything else});,因为这似乎是最简单的。
  • 拥抱异步模式,放弃同步模式。以后你会过上幸福的生活。
  • @Wright 我确实尝试在 Logger 中用 .then() 包装它。问题是在 Logger 到达 .then() 之前 main 仍在继续,这意味着它会在完全初始化之前尝试使用 Discord 客户端。
  • @trincot 目标肯定是拥抱异步模式,但是如何实现呢?您是否建议放弃将所有 Logger 逻辑包含在 Logger.js 中,并使用等待 Discord 的 login() 函数返回的承诺包装我的所有 Main.js 代码?
  • 您肯定需要将所有依赖于异步结果的代码放入回调中(用于承诺的then 回调)或async/await 构造:两者都涉及函数包装器。当然,您可以从此类回调中调用其他函数。

标签: node.js asynchronous promise synchronous discord


【解决方案1】:

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

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

例子:

//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函数解决后才会发送日志。

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

【讨论】:

    【解决方案2】:

    如果等待某个异步函数的结果然后在同一个调用者函数中使用,则首先解析结果,然后再使用。如果结果在另一个函数或模块中使用(例如,结果被分配给一个全局变量),它不会被解析。在您的情况下,如果 client.login() 异步为 client.channels 赋值,则该赋值等待,channels = client.channels 赋值会将 undefined 赋值给 channels

    如 cmets 中所述,要解决此问题,您必须使用回调或从 client.login() 返回承诺。

    您可以参考this的文章。

    【讨论】:

      猜你喜欢
      • 2018-01-12
      • 1970-01-01
      • 2022-11-24
      • 2015-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-03
      • 2013-04-01
      相关资源
      最近更新 更多