递归的基于Promise的目录读取

时间:2018-03-04 20:42:34

标签: javascript node.js asynchronous recursion promise

我有一个库,用于扫描目录以查找远程服务器上的文件。它返回一个Promise,如下所示:

client.scanRemoteDirectory(path)
  .then(files => { 

    console.log(files)

  })

我试图写一个递归方法来扫描目录和子目录。但是我遇到了一些异步问题。我的功能是这样的:

const scanDir(path) {

  // Scan the remote directory for files and sub-directories
  return client.scanRemoteDirectory(path)
    .then(files => {

      for (const file of files) {
        // If a sub-directory is found, scan it too
        if (file.type === 'directory') {

          return scanDir(file.path) // Recursive call

        }
      }
    })
}

const scanDir('some/path')
  .then(() => {
    console.log('done')
  })

然而,由于return递归方法调用前面的scanDir(),这会导致该方法仅扫描每个目录中的第一个子目录并跳过其余目录。

例如,如果结构是这样的:

/some/path
/some/path/dirA
/some/path/dirA/subdirA
/some/path/dirB
/some/path/dirB/subdirB

上述方法仅扫描:

/some/path
/some/path/dirA
/some/path/subdirA

由于该方法首先找到dirB,因此它会完全跳过dirA及其子项。

如果我只是从return电话中移除return scanDir(...),那么它会扫描所有内容。但是我的最后console.log('done')太快了,因为它是异步的。

那么我该如何解决这个问题呢?什么是正确的递归Promise方法,我仍然可以保留异步,但也可以递归扫描每个子目录?

2 个答案:

答案 0 :(得分:3)

您可能希望在这种情况下使用Promise.all来运行您的' sub'并行承诺,例如:

function scanDir(path) {

    return client.scanRemoteDirectory(path)
        .then(all => {
            const files = all.where(file => file.type !== 'directory);
            const dirs = all.where(file => file.type === 'directory);
            return Promise.all(dirs.map(dir => scanDir(dir.path)) // Execute all 'sub' promises in parallel.
                .then(subFiles => {
                    return files.concat(subFiles);
                });
        });
}

或者,您可以使用reduce功能运行您的' sub'承诺顺序:

function scanDir(path) {

    return client.scanRemoteDirectory(path)
        .then(all => {
            const files = all.where(file => file.type !== 'directory);
            const dirs = all.where(file => file.type === 'directory);
            return dirs.reduce((prevPromise, dir) => { // Execute all 'sub' promises in sequence.
                    return prevPromise.then(output => {
                        return scanDir(dir.path)
                            .then(files => {
                                return output.concat(files);
                            });
                    });
                }, Promise.resolve(files));
        });
}

Async / await绝对是最简单的解决方案:

async function scanDir(path) {

    const output = [];
    const files = await client.scanRemoteDirectory(path);
    for (const file of files) {
        if (file.type !== 'directory') {
            output.push(file);
            continue;
        }

        const subFiles = await scanDir(file.path);
        output = output.concat(subFiles);       
    }

    return output;
}

答案 1 :(得分:0)

我会使then处理程序异步,以便您可以在循环中使用await

 const scanDir(path) {
  // Scan the remote directory for files and sub-directories
  return client.scanRemoteDirectory(path)
    .then(async files => {
       for (const file of files) {
          // If a sub-directory is found, scan it too
          if (file.type === 'directory') {
            await scanDir(file.path) // Recursive call
          }
      }
   })
 }