在运行时从文件动态加载函数

Dynamically load function from files at runtime

我有一些 nodejs 代码可以充当机器人玩游戏的仲裁者。

每个机器人都是它自己的文件,有一些预定义的 functions 和固定的名称(每个机器人都会用相同的名称和参数调用它的函数,比如 PlayRound())。

现在我想在 runtime 将机器人添加到游戏中。就像我会告诉仲裁器 botName1,它会在 bots 文件夹中寻找一个名为 botName1.js 的文件,然后稍后可以调用 botName1.PlayRound()

由于 require 似乎只适用于文字静态字符串,不适用于运行时值,是否有办法做到这一点?

示例代码:

const readline = require('readline');
const readLine = readline.createInterface({ input: process.stdin });

var players = []
var playerFiles = [];

readLine.on('line', (ln) => {

    var ws = ln.split(' ');

    if (ws[0].toLowerCase() === 'add') {
        players[players.length] = ws[1];
        // I would like to add the file corresponding to the name here
    } else if (ws[0].toLowerCase() === 'list'){
        console.log('There are currently ' + players.length + ' players registered:');
        for (var p in players) {
            console.log(players[p]);
        }
    } else if (ws[0].toLowerCase() === 'start'){
        // I would like to do this here
        for (var playerFile in playerFiles) {
            playerFiles[playerFile].PlayRound();
        }
    }

});

你可以通过node的fs包获取这个

 fs.readdirSync(PATHOFDIRECTORYCONTAININGFILEs)
       .forEach((file) =>  {
        var name = file.replace('.js', '');
        if(name === botname) {
          require('./' + file).PlayRound();
        }
    });

试试这个

 const readline = require('readline');
 const fs = require('fs);
 const readLine = readline.createInterface({ input: process.stdin });

 var players = []
 var playerFiles = [];

 readLine.on('line', (ln) => {

var ws = ln.split(' ');

if (ws[0].toLowerCase() === 'add') {
    players[players.length] = ws[1];
    //
} else if (ws[0].toLowerCase() === 'list'){
    console.log('There are currently ' + players.length + ' players 
        registered:');
    for (var p in players) {
        console.log(players[p]);
    }
} else if (ws[0].toLowerCase() === 'start'){
    // I would like to do this here

   fs.readdirSync(__diname)
   .forEach((file) =>  {
       for (var playerFile in playerFiles) {
         var name = file.replace('.js', '');
         if(name === playerFile) {
         require('./' + file).PlayRound();
        }
     }
  });
});

正如@Kaito 所建议的,可以使用动态要求。但我绝不会喜欢那样,如果你不知道可能会出什么问题,那绝对不会。动态要求使您的应用程序容易出现您可能没有处理的运行时错误,例如需要一个不存在的文件(最常见的一个)。

我想在@Kaito 和@Yash 的基础上再接再厉suggested/provided。

解决方案

  1. 将您所有的机器人 files/functions 累积在一个地图中,该地图将 botname 映射到 botfilepath
  2. 按照您的逻辑,首先检查您是否有与正在加入的机器人相关的 botfile/botfunction
  3. 如果是,那么您可以在运行时安全地要求 botfile

在下面的示例中,我假设所有机器人文件都将存储在 bots 目录中。

例子

const fs        = require( "fs" ),
      path      = require( "path" ),
      botsDir   = path.join( __dirname, "bots" );

/** One time read to fetch all the bot files in bots dir */
const files         = fs.readdirSync( botsDir ),
      /** Here we create the map of bot and bot file path */
      availableBots = files
                        .reduce( ( botMap, file ) => Object.assign( botMap, { 
                            [ file.replace(".js", "") ] : path.join( botsDir, file ) 
                        } ), {} );

// Your code
const botThatWillBeJoiningAtRuntime = "BotC"; // This would be some calculation to determine bot that joins at runtime.

/** Here we safely require the bot file only if we have one corresponding to the bot that is joining */
if ( availableBots[botThatWillBeJoiningAtRuntime] ) {
    const botFunc = require( availableBots[botThatWillBeJoiningAtRuntime] );
}

这种方法的好处 -

您在整个应用程序生命周期中生成一次文件操作并累积 botfile,从而减少昂贵的文件 ios 然后 if 部分安全地需要 botfile,具体取决于应用程序是否具有 bot 文件机器人加入。

缺点是-

您需要确保加入的机器人与 bots 目录中的 botfile 同名。