基于递归 Promise 的目录读取

Jak*_*son 2 javascript recursion asynchronous node.js promise

我有一个库,可以扫描远程服务器上的文件目录。它返回一个像这样的 Promise:

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

    console.log(files)

  })
Run Code Online (Sandbox Code Playgroud)

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

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')
  })
Run Code Online (Sandbox Code Playgroud)

然而,这是有效的,因为returnscanDir()递归方法调用之前,这导致该方法仅扫描每个目录中的第一个子目录并跳过其余部分。

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

/some/path
/some/path/dirA
/some/path/dirA/subdirA
/some/path/dirB
/some/path/dirB/subdirB
Run Code Online (Sandbox Code Playgroud)

上述方法只会扫描:

/some/path
/some/path/dirA
/some/path/subdirA
Run Code Online (Sandbox Code Playgroud)

dirB由于该方法dirA首先找到,它将跳过并且完全是孩子。

如果我只是returnreturn scanDir(...)呼叫中删除,那么它会很好地扫描所有内容。但是后来我的决赛console.log('done')发生得太快了,因为它是异步的。

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

Ash*_*vis 5

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);
                });
        });
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用该reduce函数按顺序运行您的“子”承诺:

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));
        });
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)