【问题标题】:Which is the most efficient way to iterate a directory?迭代目录的最有效方法是什么?
【发布时间】:2017-11-21 00:18:41
【问题描述】:

假设我有一个目录 foo,其中包含一些子目录。这些子目录中的每一个都有我想要处理的 0 到 5 个可变长度的文件。我的初始代码如下所示:

    pool.query(`
      SET SEARCH_PATH TO public,os_local;
    `).then(() => fs.readdirSync(srcpath)
        .filter(file => fs.lstatSync(path.join(srcpath, file)).isDirectory())
        .map(dir => {
          fs.access(`${srcpath + dir}/${dir}_Building.shp`, fs.constants.R_OK, (err) => {
            if (!err) {
              openShapeFile(`${srcpath + dir}/${dir}_Building.shp`).then((source) => source.read()
.then(function dbWrite (result) {
              if (result.done) {
                console.log(`done ${dir}`)
              } else {
    const query = `INSERT INTO os_local.buildings(geometry,
                  id,
                  featcode,
                  version)
                  VALUES(os_local.ST_GeomFromGeoJSON($1),
                  $2,
                  $3,
                  $4) ON CONFLICT (id) DO UPDATE SET
                    featcode=$3,
                    geometry=os_local.ST_GeomFromGeoJSON($1),
                    version=$4;`
                return pool.connect().then(client => {
                  client.query(query, [geoJson.split('"[[').join('[[').split(']]"').join(']]'),
                    result.value.properties.ID,
                    result.value.properties.FEATCODE,
                    version
                  ]).then((result) => {
                    return source.read().then(dbWrite)
                  }).catch((err) => {
                    console.log(err,
                      query,
                      geoJson.split('"[[').join('[[').split(']]"').join(']]'),
                      result.value.properties.ID,
                      result.value.properties.FEATCODE,
                      version
                    )
                    return source.read().then(dbWrite)
                  })
                  client.release()
                })
              }
            })).catch(err => console.log('No Buildings', err))
            }
          })

          fs.access(`${srcpath + dir}/${dir}__ImportantBuilding.shp`, fs.constants.R_OK, (err) => {
            //read file one line at a time
            //spin up connection in pg.pool, insert data
          })

          fs.access(`${srcpath + dir}/${dir}_Road.shp`, fs.constants.R_OK, (err) => {
            //read file one line at a time
            //spin up connection in pg.pool, insert data
          })

          fs.access(`${srcpath + dir}/${dir}_Glasshouse.shp`, fs.constants.R_OK, (err) => {
            //read file one line at a time
            //spin up connection in pg.pool, insert data
          })

          fs.access(`${srcpath + dir}/${dir}_RailwayStation.shp`, fs.constants.R_OK, (err) => {
            //read file one line at a time
            //spin up connection in pg.pool, insert data
          })
        })

这主要是可行的,但最终不得不等待最长的文件在每个子目录中被完全处理,导致实践中总是只有 1 个与数据库的连接。

有没有一种方法可以重新构建它以更好地利用我的计算资源,同时限制活动 postgres 连接的数量并强制代码等待连接可用? (我在 node-postgres 的 pg poolConfig 中将它们设置为 20)

【问题讨论】:

  • 从未听说过 Bromise...
  • 为什么这里的性能是个问题?如果您正在配置数据库,那么花费多长时间并不重要。
  • 为什么这是一个承诺?你可能没有收到任何错误,因为承诺正在吞噬它们。
  • "它最终不得不等待最长的文件在每个子目录中被完全处理" - 呃,你展示给我们的代码不会等待任何东西(但同步子目录遍历)?!请发布您的实际代码,包括实际处理文件数据的非常重要的行。
  • 已编辑以包含文件访问的内容。

标签: javascript asynchronous promise


【解决方案1】:

如果您需要在一定时间内依次处理您的文件,那么您可以使用Streams、timers(用于调度)和 process.nextTick()。 有 great manual 用于理解 nodejs 中的流。

【讨论】:

    【解决方案2】:

    这是一个使用生成器获取目录内容的示例。您可以立即开始获取前几个文件,然后使用异步代码并行处理文件。

    // Dependencies
    const fs = require('fs');
    const path = require('path');
    
    // The generator function (note the asterisk)
    function* getFilesInDirectory(fullPath, recursive = false) {
        // Convert file names to full paths
        let contents = fs.readdirSync(fullPath).map(file => {
            return path.join(fullPath, file);
        });
    
        for(let i = 0; i < contents.length; i++) {
            const childPath = contents[i];
            let stats = fs.statSync(childPath);
            if (stats.isFile()) {
                yield childPath;
            } else if (stats.isDirectory() && recursive) {
                yield* getFilesInDirectory(childPath, true);
            }
        }
    }
    

    用法:

    function handleResults(results) {
        ... // Returns a promise
    }
    
    function processFile(file) {
        ... // Returns a promise
    }
    
    var files = getFilesInDirectory(__dirname, true);
    var result = files.next();
    var promises = [];
    while(!result.done) {
        console.log(result.value);
        file = files.next();
        // Process files in parallel
        var promise = processFile(file).then(handleResults);
        promises.push(promise);
    }
    
    promise.all(promises).then() {
        console.log(done);
    }
    

    【讨论】:

    • 使用生成器并不能真正帮助并行处理文件。
    • 那个丑陋的while(1)循环应该是一个简单的yield* getFilesInDirectory(childPath, true);
    • @Bergi - 已修复。谢谢!
    猜你喜欢
    • 2019-08-31
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 2023-01-11
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    相关资源
    最近更新 更多