【问题标题】:Confused about Node's event loop by using promises使用 Promise 对 Node 的事件循环感到困惑
【发布时间】:2021-01-13 23:21:24
【问题描述】:

我正在编写一个递归函数,它创建一个选定文件目录的对象树。我的代码有效,但顺序错误。我看不到我的代码的输出。代码如下:

const fs = require("fs");

const basePath = process.argv[2];
const result = {};

const isDirectory = path => {
  return new Promise((resolve, reject) => {
    fs.lstat(path, (err, stats) => {
      if (err) reject("No such file or Directory");

      resolve(stats.isDirectory());
    });
  });
};

const createTree = (path, target) => {
  return new Promise((reject, resolve) => {
    fs.readdir(path, (err, list) => {
      for (const item of list) {
        const currentLocation = `${path}/${item}`;
        isDirectory(currentLocation).then(isDir => {
          console.log(result); //I CAN SEE THE RESULT HERE
          if (!isDir) {
            target[item] = true;
          } else {
            target[item] = {};
            resolve(createTree(currentLocation, target[item]));
          }
        });
      }
    });
    reject("Somthing went wrong while getting the list of files");
  });
};

createTree(basePath, result)
  .then(() => console.log("result --->", result)) //BUT NOT HERE
  .catch(err => console.log("Consume Error ==>", err));

我也使用 async await 完成了它,但我很好奇为什么它不适用于 Promise。

这是async await 的完整工作示例:

const fs = require("fs");

const basePath = process.argv[2]; //Getting the path
const result = {};

//Function to check my path is exist and it's a directory
const isDirectory = async path => {
  try {
    const stats = await fs.promises.lstat(path); //Used istat to get access to the "isDirectory()" method
    return stats.isDirectory();
  } catch (error) {
    throw new Error("No such file or Directory");
  }
};

//Recursive function that should create the object tree of the file system
const createTree = async (path, target) => {
  try {
    const list = await fs.promises.readdir(path);
    for (const item of list) {
      const currentLocation = `${path}/${item}`;
      const isDir = await isDirectory(currentLocation);
      //If it's a file, then assign it to true
      //Otherwise create object of that directory and do the same for it
      if (!isDir) {
        target[item] = true;
      } else {
        target[item] = {};
        await createTree(currentLocation, target[item]);
      }
    }
  } catch (err) {
    console.log("Somthing went wrong while getting the list of files");
  }
};

//Consuming the createTree function
(async () => {
  try {
    await createTree(basePath, result);
    console.log(result);
  } catch (error) {
    console.log(error.message);
  }
})();

【问题讨论】:

  • 我没有看到你在第一个版本中调用 resolve,在 createTree 中。您需要在某个地方解决承诺以使其发挥作用。使用 async/await 当然“它是自动完成的”
  • createTreenew Promise((reject, resolve) => { --> new Promise((resolve, reject) => {,另外你也不要打电话给resolve
  • 你真的应该看看const fs = require('fs/promises');
  • @sp00m 我尝试调用resolve并将递归调用放入其中,它返回的结果与没有resolve的情况相同,在我尝试过的其他地方它只是拒绝。你能帮我吗?感谢您的关注))
  • 你不能多次resolve一个Promise。这就是您 for 循环正在尝试做的事情。您复制previous question 有什么原因吗?那里提供的答案有什么问题?

标签: javascript node.js recursion promise


【解决方案1】:

我只是好奇是否可以做同样的事情,但只能通过承诺。

asyncawait 只是语法糖,可以更轻松地使用基于 Promise 的程序。任何依赖于这些关键字的程序都可以重写以不使用它们 -

// main.js

import { readdir } from "fs/promises"
import { join } from "path"

function createTree (init = ".")
{ const one = path => p =>
    p.isDirectory()
      ? many(join(path, p.name)).then(r => ({ [p.name]:  r }))
      : { [p.name]: true }

  const many = path =>
    readdir(path, { withFileTypes: true })
      .then(r => Promise.all(r.map(one(path))))
      .then(r => Object.assign(...r))

  return many(init)
}

createTree(".")
  .then(v => console.log(JSON.stringify(v, null, 2)))
  .catch(console.error)

现在让我们添加一些示例文件,以便我们可以看到我们的程序正常工作 -

$ yard add immutable    # (any example package)
$ node main.js

输出 -

{
  "main.js": true,
  "node_modules": {
    ".yarn-integrity": true,
    "immutable": {
      "LICENSE": true,
      "README.md": true,
      "contrib": {
        "cursor": {
          "README.md": true,
          "__tests__": {
            "Cursor.ts.skip": true
          },
          "index.d.ts": true,
          "index.js": true
        }
      },
      "dist": {
        "immutable-nonambient.d.ts": true,
        "immutable.d.ts": true,
        "immutable.es.js": true,
        "immutable.js": true,
        "immutable.js.flow": true,
        "immutable.min.js": true
      },
      "package.json": true
    }
  },
  "package.json": true,
  "yarn.lock": true
}

如果您希望将init 路径包含在树中,只需稍作修改 -

// main.js

import { readdir } from "fs/promises"
import { join, basename } from "path" // !

function createTree (init = ".")
{ const one = path => p =>
    p.isDirectory()
      ? many(join(path, p.name)).then(r => ({ [p.name]:  r })) // !
      : { [p.name]: true }

  const many = path =>
    readdir(path, { withFileTypes: true })
      .then(r => Promise.all(r.map(one(path))))
      .then(r => Object.assign(...r)) // !
      .then(r => ({ [basename(path)]: Object.assign(...r) })) // !

  return many(init)
}

现在树包含了我们的初始路径 -

createTree(".")
  .then(v => console.log(JSON.stringify(v, null, 2)))
  .catch(console.error)
{ ".":      // <- starting path 
  { ... }
}

要了解如何使用异步生成器编写此程序,请参阅original Q&A

【讨论】:

  • 我喜欢这种使用yarn add 演示基于目录/文件的代码的方式。非常干净。
  • “简单语法糖”和所有允许的头韵。
猜你喜欢
  • 2018-10-03
  • 2014-12-07
  • 2016-07-13
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 2023-01-13
  • 2016-02-21
  • 2020-12-11
相关资源
最近更新 更多