for 循环添加到数组后返回结果

时间:2021-05-04 19:52:01

标签: javascript node.js asynchronous

我试图在 forEach 循环完成向其中添加项目后返回一个数组。我尝试了几种方法,但它们都返回一个空数组或一个未解决的承诺。

我的代码

class handleFiles {
  constructor() {
    this.promiseFiles = [];
    this.promiseFolders = [];
  }
  async returnFilesAndFolders() {
    fs.readdir("uploads", (err, files) => {
      files.forEach((file) => {
        if (fs.statSync(path.resolve(`uploads/${file}`)).isFile()) {
          this.promiseFiles.push(file);
        } else if (fs.statSync(path.resolve(`uploads/${file}`)).isDirectory()) {
          this.promiseFolders.push(file);
        } else {
        }
      });
    });
    let combined = {
      sendFiles: Promise.all(this.promiseFiles),
      sendFolders: Promise.all(this.promiseFolders),
    };
    return combined;
  }
}

给班级打电话

app.get("/:dir?", (req, res) => {
  let dir = req.params.dir;
  console.log(dir);
  let fileHandler = new handleFiles();

  fileHandler.returnFilesAndFolders().then((data) => {
    console.log(data);
    res.render("index", {
      data: data,
    });
  });
});

2 个答案:

答案 0 :(得分:0)

您的原始实现有几个时间问题。您的 .forEach() 循环不会等待其中的任何异步操作完成,因此您最终会在数组填充之前调用 Promise.all()。我建议使用具有内置承诺支持的 fs 操作,并且还使用带有 withFileTypesfs.promises.readdir() 选项:

class handleFiles {
  constructor() {
    this.promiseFiles = [];
    this.promiseFolders = [];
  }
  async getFilesAndFolders() {
      let files = await fs.promises.readdir("uploads", {withFileTypes: true});
      for (let file of files) {
          if (file.isFile()) {
              this.promiseFiles.push(file.name);
          } else if (file.isDirectory()) {
              this.promiseFolders.push(file.name);
          }
      }
      return {
          sendFiles: this.promiseFiles,
          sendFolders: this.promiseFolders
      }
  }
}

注意:这个实现有点奇怪,因为它同时填充 this.promiseFilesthis.promiseFolders,并且返回一个解析为包含该数据的对象的承诺。我真的建议你做一个或另一个。此外,由于添加到 this.promiseFilesthis.promiseFolders 的这种副作用编程,如果您多次调用 getFilesAndFolders(),它将创建重复项。

也许你的实例数据中根本不需要这些数据,该方法可以只返回一个解析为对象的承诺:

class handleFiles {
  constructor() {
  }
  async fillFilesAndFolders() {
      const sendFiles = [], sendFolders = [];
      let files = await fs.promises.readdir("uploads", {withFileTypes: true});
      for (let file of files) {
          if (file.isFile()) {
              sendFiles.push(file.name);
          } else if (file.isDirectory()) {
              sendFolders.push(file.name);
          }
      }
      return {
          sendFiles,
          sendFolders
      };
  }
}

但是,现在这只是一个与类没有任何关系的静态方法,所以它可能只是一个独立函数或静态方法。

第二个解决方案中的分步说明:

  1. 声明局部变量 promiseFolderspromiseFiles
  2. 调用 await fs.promises.readdir() 以便我们可以使用 readdir 的 promise 接口。
  3. 传递 withFileTypes 选项,这样我们就可以访问 isFile()isDirectory(),而无需对每个文件进行额外调用。
  4. 遍历 files 的列表并使用文件名填充所需的 promiseFilespromiseFolders 数组。
  5. 构建返回的数据结构并返回它(这将是从父 async 函数返回的 promise 的解析值。

答案 1 :(得分:-2)

async returnFilesAndFolders() {
  // Return the fs.readdir promise.
  return fs.readdir("uploads").then((files) => {

    // Collect the files into files and folders.
    return files.reduce((acc, file) => {
      // Get the status of the file.
      const stat = fs.statSync(path.resolve(`uploads/${file}`));

      if (stat.isFile()) {
        acc.sendFiles.push(file);
      }
      elseif (stat.isDirectory()) {
        acc.sendFolders.push(file);
      }

      return acc;
    }, {sendFiles: [], sendFolders: []})
  });
}

file 只是一个字符串,不是承诺,所以不需要解析。 fs.readdir 返回一个 promise,因此它可以与 .then 一起使用。进程退出时,中间的 promiseFiles 和 promiseFolders 将为空,但不需要它们,因为 file 不是承诺。