【问题标题】:Crawl recursively and write to file in asynchronous manner递归爬取并以异步方式写入文件
【发布时间】:2020-02-28 04:47:11
【问题描述】:

我对异步代码完全陌生,所以我现在有点不知所措。

我正在做的是递归地异步抓取存档,以检测给定存档中的文件路径。然后我要做的是将检测到的所有文件路径写入单个文件。但是,当我执行代码时,它不会正确地将它们写入文件。我假设这是由于多次写入彼此重合。

data.json 之前

{
  "K": {
    "files": []
  }
}

data.json 之后

{
  "K": {
    "files": [
      {
        "name": "Testing.txt",
        "bytes": 1648,
        "path": "K:\\Texts\\Testing.txt"
      }
    ]
  }
}      }
    ]
  }
}
}.txt"
      }
    ]
  }
}    }
    ]
  }
}

我显然可以同步编写代码,但为了提高效率,我更喜欢异步执行所有这些操作。尽管如此,我真的不确定解决这个问题的最佳方法。我知道这样做的一种方法是等到最后一个文件被抓取并推送(然后将新属性写入文件),但我不知道如何在异步环境中有效地检测到它?我可以每隔一段时间检查一次,但在我看来这是一种愚蠢的方法。

以下是导致问题的异步代码。

// Scan directories looking for target file types.
async function scanDirs(){
  const
    config = await fsp.readFile('./config.json', 'utf8'),
    archives = JSON.parse(config).archives,
    { join } = require('path'),
    traverse = async (path) => {
      try {
        const stats = await fsp.stat(path)
        if (stats.isDirectory()){
          const childPaths = await fsp.readdir(path)
          for (const childPath of childPaths){
            const
              fullPath = join(path, childPath)
            traverse(fullPath)
          }
        } else if (stats.isFile()) {
          const
            fileTypes = config.fileTypes,
            fileExt = path.substring(path.lastIndexOf('.')+1)
          if (fileTypes.includes(fileExt)){
            const
              data = await fsp.readFile('./data.json', 'utf8'),
              json = JSON.parse(data),
              drive = path.substring(0,1),
              files = json[drive].files,
              stat = await fsp.stat(path),
              newFile = {
                "path": path,
                "name": path.substring(path.lastIndexOf('\\')+1),
                "bytes": stat.size
              }
            files.push(newFile)
            fsp.writeFile('./data.json', JSON.stringify(json, null, 2))
          }
        }
      }
      catch (error){
        console.error(error)
      }
    }

  for (const path of archives){
    traverse(path)
  }
}

任何帮助将不胜感激。

【问题讨论】:

  • 在您的函数中递归地构建数据结构,将其作为(承诺)对象返回。然后等待,将其序列化,并仅将其写入文件一次。
  • “我不关心结构”是什么意思?乱码输出肯定不行吗?
  • @Bergi 所有即时记录都是我粘贴到数组中的文件的路径
  • @Bergi 我很困惑如何将traverse 作为承诺返回。我从for 循环中无限次调用traverse,而循环又无限次调用自身。我对 Promise 和异步代码是全新的,所以像我 5 岁一样向我解释一下

标签: javascript node.js asynchronous async-await fs


【解决方案1】:

我知道这样做的一种方法是等到最后一个文件被抓取后再写入,但我不知道如何在异步环境中有效地检测到这一点?

您可以使用Promise.all 来等待多个承诺:

const { join } = require('path');
async function searchFiles(path, fileTypes) {
  try {
    const stats = await fsp.stat(path)
    if (stats.isDirectory()){
      const childPaths = await fsp.readdir(path)
      const promises = childPaths.map(childPath =>
        searchFiles(join(path, childPath), fileTypes)
      );
      const results = await Promise.all(promises);
      return [].concat(...results);
    } else if (stats.isFile()) {
      const fileExt = path.substring(path.lastIndexOf('.')+1)
      if (fileTypes.includes(fileExt)) {
        return [{
          "path": path,
          "name": path.substring(path.lastIndexOf('\\')+1),
          "bytes": stats.size
        }];
      }
    }
  } catch(e) {
    // ignore. Log?
  }
  return [];
}
async function readJson(path) {
  return JSON.parse(await fsp.readFile(path, 'utf8'));
}

// Scan directories looking for target file types.
async function scanDirs() {
  try {
    const [config, data] = await Promise.all([readJson('./config.json'), readJson('./data.json')]);
    const results = await Promise.all(config.archives.map(path => searchFiles(path, config.fileTypes)));
    for (const newFile of [].concat(...results)) {
      const drive = newFile.path.substring(0,1);
      data[drive].files.push(newFile);
    }
    fsp.writeFile('./data.json', JSON.stringify(data, null, 2));
  } catch (error){
    console.error(error)
  }
}

顺便说一句,您可能要考虑使用 path module 中的 basenameextname 而不是字符串操作,但鉴于这是一个仅限 Windows 的程序(使用驱动器号),它可能并不重要.

【讨论】:

  • 我现在要玩这个,这样我就可以了解发生了什么。感谢您的帮助
  • 所以基本上任何异步函数本质上都是一个承诺,对吗?
  • 另外,当我从另一个函数调用readJson('path') 时,readJson 在新上下文中是一个期待的承诺吗?例如,async someOtherFunction(){ const c = readJson('./config.json'), d = c.archives }d 将始终等待 c 在这种情况下或无论在何种情况下?
  • @PrimitiveNom 每个asynchronous 函数返回一个承诺,是的。
  • @PrimitiveNom 上下文不会等到你明确地await 承诺。不,您示例中的d 不会等待c,它不知道也不关心readJson 做了什么。 “等待上下文”只是当前的async function,它的执行被暂停(就像一个生成器),直到当前等待的承诺得到解决。其他函数中的其他代码不受此影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多