递归和异步函数不 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 = commands
或 type = 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
中的事件按以下顺序发生:
控制台日志I GOT HERE 1
load_js_files
与 commands
参数一起出现(假设我还没有解决递归承诺,但它至少应该 运行 一次)
控制台日志I GOT HERE 2
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_files
在 load_content
) 中调用。
奖励积分:
如果你能帮助我理解递归函数中的承诺,加分。我读过
- https://blog.scottlogic.com/2017/09/14/asynchronous-recursion.html
- https://www.bennadel.com/blog/3201-exploring-recursive-promises-in-javascript.htm
和
- https://medium.com/@wrj111/recursive-promises-in-nodejs-769d0e4c0cf9
但还没有完全通过。
尝试实现 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
。
情况:
我有一个函数 运行 在我的代码开头 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 = commands
或 type = 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
中的事件按以下顺序发生:
控制台日志
I GOT HERE 1
load_js_files
与commands
参数一起出现(假设我还没有解决递归承诺,但它至少应该 运行 一次)控制台日志
I GOT HERE 2
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_files
在 load_content
) 中调用。
奖励积分:
如果你能帮助我理解递归函数中的承诺,加分。我读过
- https://blog.scottlogic.com/2017/09/14/asynchronous-recursion.html
- https://www.bennadel.com/blog/3201-exploring-recursive-promises-in-javascript.htm 和
- https://medium.com/@wrj111/recursive-promises-in-nodejs-769d0e4c0cf9
但还没有完全通过。
尝试实现 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
。