递归的基于Promise的目录读取

问题描述 投票:1回答:2

我有一个库,用于扫描目录以查找远程服务器上的文件。它返回一个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方法,我仍然可以保留异步,但也可以递归扫描每个子目录?

javascript node.js asynchronous recursion promise
2个回答
3
投票

您可能希望在这种情况下使用Promise.all并行运行“子”承诺,例如:

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;
}

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
          }
      }
   })
 }
© www.soinside.com 2019 - 2024. All rights reserved.