在Node.js中使用Promise调用异步递归函数

在Node.js中使用Promise调用异步递归函数,node.js,asynchronous,promise,callback,electron,Node.js,Asynchronous,Promise,Callback,Electron,首先,我需要说我对electron和node.js非常陌生 我正在做的是,我试图将一个包含一些目录数据的数组发送到我的浏览器,而这个异步(错误:数组为空) 问题应该是我的函数“ScandDirectories(path)”。 如果我将其设置为非递归(ScandDirectories(res)->仅返回res),则它对1级目录非常有效,但递归则不行 所以我想我需要像上面的函数一样返回一个新的承诺? 我试过了,但不知道它是如何工作的,也不知道我的语法应该是什么 希望你能帮助我。 西蒙 main.js

首先,我需要说我对electron和node.js非常陌生

我正在做的是,我试图将一个包含一些目录数据的数组发送到我的浏览器,而这个异步(错误:数组为空)

问题应该是我的函数“ScandDirectories(path)”。 如果我将其设置为非递归(ScandDirectories(res)->仅返回res),则它对1级目录非常有效,但递归则不行

所以我想我需要像上面的函数一样返回一个新的承诺? 我试过了,但不知道它是如何工作的,也不知道我的语法应该是什么

希望你能帮助我。
西蒙

main.js:
从ipcmain调用函数

ipcMain.on('fileTree', (event, arg) => {
  let fileDirectory = helperFile.getDirectories(arg);
  fileDirectory.then(function(result) {
    event.reply('fileTree', result);
  }, function(err) {
    console.log(err);
  })
})
files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
    getDirectories: async function(path) {
       return new Promise(function (resolve, reject) {
           try {
               resolve(scanDirectories(path));
           } catch {
               reject('Error');
           }
       });
    }
};

async function scanDirectories(path) {
    const dirents = await readdir(path, {withFileTypes: true});
    const files = dirents.map((dirent) => {
        const res = resolvePath(path, dirent.name);
        return dirent.isDirectory() ? scanDirectories(res) : res;
    });
    return Array.prototype.concat(...files);
}

您可以尝试以下方法,生成一系列承诺:

files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
  // This is an async function so don’t need internal promise
  getDirectories: async function(path) {
    try {
      const dirs = await scanDirectories(path);
      return dirs;
    }
    catch {
      throw new Error('Error');
    }
  }
};

async function scanDirectories(path) {
  const dirents = await readdir(path, {withFileTypes: true});

  // Generate array of promises
  const promises = dirents.map(dirent => {
    const res = resolvePath(path, dirent.name);
    return dirent.isDirectory()
      ? scanDirectories(res)
      : Promise.resolve(res);
  });

  // Await all promises
  const files = await Promise.all(promises);
  return Array.prototype.concat(...files);
}

如果您在没有等待的情况下调用
异步
函数,您将收到一个承诺

您的事件处理程序使用
然后
处理这种OK(它在错误处理方面有问题),但是您对
ScandDirectories
的递归调用没有

等待异步函数解析的最简单方法是使用
wait

因此,此更改使递归调用正确:

return dirent.isDirectory() ? (await scanDirectories(res)) : res;
注意添加了
wait

但是,“Array.map”不是为在异步函数中使用而设计的。它将同步调用它们,并创建一系列承诺。不是你想要的

此外,此代码不必要地复杂,将承诺包装在承诺中,并以不起作用的方式使用
try/catch

    getDirectories: async function(path) {
       return new Promise(function (resolve, reject) {
           try {
               resolve(scanDirectories(path));
           } catch {
               reject('Error');
           }
       });
    }
只需直接从原始事件处理程序调用
scanDirectories
,并使事件处理程序成为
async
函数,许多代码就会消失

一般来说:如果必须处理异步内容,请编写一个
async
函数,并始终在调用该函数的函数中等待它,即使该函数本身也是如此。您可以在任何地方编写异步函数,即使它们是事件处理程序、Express routes或其他不会使用它们解析的承诺的上下文

以下是经过简化和更正的原始代码,但工作方式基本相同:

ipcMain.on('fileTree', async (event, arg) => {
  try {
    event.reply('fileTree', await helperFile.scanDirectories(arg);
  } catch (e) {
    console.log(e);
  }
});

// files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
  scanDirectories: async function(path) {
    const dirents = await readdir(path, { withFileTypes: true });
    const files = [];
    for (const dirent of dirents) {
      const res = resolvePath(path, dirent.name);
      if (dirent.isDirectory()) {
        files = files.concat(await scanDirectories(res));
      } else {
         files.push(res);
      }
    });
    return files;
  }
}

看起来是个好问题,但是你能改进格式吗?避免错误!为什么要抛出一个毫无意义的
新错误('Error')
?@Steve您的解决方案运行良好!谢谢你的快速回答。@Simon没问题-很高兴有帮助:)这不起作用,
Wait scanDirectories
不在
异步函数中
-它在
映射中
callbackOops是的。Map需要一个普通的同步函数。我会更正我的答案。“for…of”可以安全地与async/await一起使用,因此我通常推荐一种基于它的解决方案。您也可以将承诺返回函数(包括
async
函数)与
map
一起使用,您只需返回一组承诺,并且需要使用
promise.all
。两者都是有效的解决方案。这是正确的,但我想提出一个解决方案,不需要考虑太多的承诺。不过,这完全是一种风格上的差异。