我有一个库,用于扫描目录以查找远程服务器上的文件。它返回一个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方法,我仍然可以保留异步,但也可以递归扫描每个子目录?
您可能希望在这种情况下使用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;
}
我会使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
}
}
})
}