递归和异步函数不 respect/wait for promise

Recursion & Async function does not respect/wait for promise

情况:

我有一个函数 运行 在我的代码开头 load_content:

async function load_content() {
    console.log("I GOT HERE 1");
    await load_js_files("./cmds/","commands")
    console.log("I GOT HERE 2");
    await load_js_files("./events/","events");
}

这个函数调用load_js_files两次,load_js_files是一个递归函数,它为指定目录中的每个目录调用自己,'requiring'每个文件找到并做不同的事情如果type = commandstype = events.

函数 load_js_files 看起来像:

function load_js_files(dir,type){
    fs.readdir(dir, (e, files) => {
        if(e) console.error(e);

        let jsfiles = files.filter(f => f.split(".").pop() === "js");
        if(jsfiles.length <= 0){
            console.log(`No commands to load from ${dir}!`);
            return;
        }
        for(const file of files){
            if(fs.lstatSync(dir+file).isDirectory()){
                load_js_files(dir+file+"/",type)
            }
        }
        if(type === "commands"){
            console.log("\x1b[4m%s\x1b[0m",`Loading ${jsfiles.length} commands from ${dir} ...`);
            jsfiles.forEach((f,i) => {
                let command = require(`${dir}${f}`);
                console.log(`${i + 1}: ${f} loaded!`);
                bot.commands.set(command.info.name, command);
            });
        } else if (type === "events"){
            console.log("\x1b[4m%s\x1b[0m",`Loading ${jsfiles.length} events from ${dir} ...`);
            jsfiles.forEach((f,i) => {
                let event = require(`${dir}${f}`);
                console.log(`${i + 1}: ${f} loaded!`);
                let commands = [];
                for(const cmd of bot.commands){
                    if(cmd[1].data) commands.push(cmd[1].data.toJSON());
                }
                if(event.once){
                    bot.once(event.name, (...args) => event.execute(...args, commands));
                } else {
                    bot.on(event.name, (...args) => event.execute(...args, commands));
                }
            });
        } else {
            console.log(log_Red,"FUNCTION 'load_js_files' CALLED WITH INCORRECT 'type'.")
        }
    });
    return new Promise((resolve,reject) => resolve("DONE"));
}

我希望 load_content 中的事件按以下顺序发生:

  1. 控制台日志I GOT HERE 1

  2. load_js_filescommands 参数一起出现(假设我还没有解决递归承诺,但它至少应该 运行 一次)

  3. 控制台日志I GOT HERE 2

  4. load_js_files 再次出现,但带有 events 参数。

问题:

在运行宁load_js_files要求type = event时,变量(bot.commands)未定义。 bot.commands 是在 load_js_files 调用期间根据上面的第 2 步分配的值。

从我可以调试的情况来看,初始函数 load_content 不尊重 async/await 的(我的理解),所以我假设我做错了承诺。

但是在我的控制台中,两个 console.log 语句在函数完成之前立即执行 &:

I GOT HERE 1
I GOT HERE 2
Loading 3 commands from ./cmds/ ...
1: createtables.js loaded!
2: ping.js loaded!
3: sqltest.js loaded!
Loading 1 events from ./events/ ...
1: ready.js loaded!
Loading 1 commands from ./cmds/settings/ ...
1: set.js loaded!

我尝试过的:

我试过上面提到的代码,另外我试过将 load_js_files 的第二个 运行 包装在 .then() 中,我试过回调函数 & 我'我也尝试过嵌套 Promise,但是 运行 进入问题,因为 load_js_files 正在递归调用自身。

我很难理解这些 Promise 是否适用于这种类型的递归(load_js_files 的所有递归必须 在第二个 load_js_filesload_content) 中调用。

奖励积分:

如果你能帮助我理解递归函数中的承诺,加分。我读过

但还没有完全通过。

尝试实现 David 的回答:

这会导致错误,我认为与 fs.readdir(dir) 需要回调有关。

错误:TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined

async function load_js_files_async_test(dir,type){
    const files = fs.readdir(dir);
    for (const file of files) {
        const file_info = await lstat(dir + file);
        if(file_info.isDirectory()){
            await load_jsfiles_async_test(dir + file + "/", type);
        } else {
            console.log("Thanks David!");
        }
    }
}

函数

fs.readdir

是一个非阻塞函数,这意味着您插入的代码不会 'awaited' 完成,您可以尝试使用 fs.readdirSync 并删除您的 return new Promise()

does not respect/wait for promise

当然可以。这是它正在等待的承诺:

new Promise((resolve,reject) => resolve("DONE"))

Promise 当然会非常快(并且是同步的)完成,然后代码会转到下一个任务。但是代码中任何地方都没有等待的是这个异步操作:

fs.readdir(dir, (e, files) => {
  //...
});

此对 readdir 的调用是一个异步操作,因此在当前线程完成其正在执行的所有操作之前不会调用回调函数。其中包括正在等待的“承诺”(不执行任何异步操作)以及 console.log 语句和 load_js_files.

的下一次调用

幸运的是,Node 也提供了 Promise-based versions 这些操作。

稍微简化一下原始代码,想象一下这个结构:

async function load_js_files(dir, type) {
  const files = await readdir(dir);
  for (const file of files) {
    const fileInfo = await lstat(dir + file);
    if(fileInfo.isDirectory()) {
      await load_js_files(dir + file + "/", type)
    }
  }
  // etc.
}

如您所见,这“读取”起来更像是同步操作。这里的想法是删除回调函数的使用,这本质上会让你感到困惑,让“等待”变得更加困难。现在 load_js_files 函数中的所有逻辑都直接 load_js_files 函数中,而不是在其他匿名回调函数中。然后该逻辑逐步进行,等待每个异步操作。

然后您可以 await 按预期调用 load_js_files