Javascript Node.js在递归循环中返回承诺
我很难让循环以正确的顺序返回嵌套的承诺。 我试图递归地循环遍历一个包含子目录的目录,以获取文件并将其复制到一个新目录。此功能发生在承诺链中,因为复制文件后,其他进程需要按顺序完成 下面的代码可以工作,但当I console.log退出Javascript Node.js在递归循环中返回承诺,javascript,node.js,recursion,promise,fs,Javascript,Node.js,Recursion,Promise,Fs,我很难让循环以正确的顺序返回嵌套的承诺。 我试图递归地循环遍历一个包含子目录的目录,以获取文件并将其复制到一个新目录。此功能发生在承诺链中,因为复制文件后,其他进程需要按顺序完成 下面的代码可以工作,但当I console.log退出之后的字符串时,
之后的字符串时,
代码是:
let newDirectory = /// helper function that returns promise of mkdirSync(exampleDir)
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
console.log(result);
});
console.log('this should fire after the "result" from above');
//// rest of promises in promise chain
});
我调用的递归函数“getAllFiles”遍历“TestDir”及其子文件夹,并将文件复制到“ExampleDir”
复制文件的助手在复制文件后返回承诺
exports.copyFile = function(file, destDir){
return new Promise(function ( resolve, reject ){
fs.copyFile(file, destDir, (err) => {
if(err) reject( err );
else resolve('Successfully copied');
});
});
}
这实际上似乎是可行的,但我担心它无法处理大量数据,因为
console.log('this should fire after the "result" from above');
在其他日志之前触发。控制台看起来像:
this should fire after the "result" from above
done with Promises /// how ever many files are copied
[ 'Successfully copied'] //// with a length of however many files are copied)
done with Promises //// this is fired once
这是一个预期的日志,还是所有的承诺都应该在“'this should fire after the“result”from over”行被记录之前解决并记录?如果您想在承诺之后控制台日志,您必须在中执行。然后()
Promise.all(promises).then(函数(结果){
决心(结果);
log('done with promissions');
})
。然后(()=>console.log(“这应该在上面的“结果”之后触发”))
.catch((错误)=>拒绝(错误));
这是因为Promise
s是非阻塞的,它之后的任何操作都不会等到它完成后再执行。fs/promises和fs.Dirent
let newDirectory = /// helper function that returns promise of mkdirSync(exampleDir)
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
console.log(result);
});
console.log('this should fire after the "result" from above');
//// rest of promises in promise chain
});
这是一个高效、无阻塞的ls
程序,使用节点的快速对象和模块。这种方法允许您跳过每个路径上浪费的fs.exist
或fs.stat
调用
使用async
和await
,我们可以避免过多地考虑如何具体实现承诺-
// main.js
import { readdir } from "fs/promises"
import { join } from "path"
async function* ls (path = ".")
{ yield path
for (const dirent of await readdir(path, { withFileTypes: true }))
if (dirent.isDirectory())
yield* ls(join(path, dirent.name))
else
yield join(path, dirent.name)
}
async function* empty () {}
async function toArray (iter = empty())
{ let r = []
for await (const x of iter)
r.push(x)
return r
}
toArray(ls(".")).then(console.log, console.error)
让我们获取一些示例文件,这样我们就可以看到ls
正在工作-
$warn add immutable#(只是一些示例包)
$node main.js
[
'.',
'main.js',
“节点_模块”,
“节点\单元/纱线完整性”,
“节点_模块/不可变”,
“节点\模块/不可变/许可证”,
“node_modules/immutable/README.md”,
“节点\模块/不可变/可控”,
“节点\模块/不可变/控制/游标”,
'node_modules/immutable/contrib/cursor/README.md',
'节点\模块/不可变/contrib/cursor/\测试\',
'节点\模块/immutable/contrib/cursor/\测试\测试\ cursor.ts.skip',
“node_modules/immutable/contrib/cursor/index.d.ts”,
'node_modules/immutable/contrib/cursor/index.js',
“节点\模块/不可变/dist”,
'node_modules/immutable/dist/immutable nonambient.d.ts',
'node_modules/immutable/dist/immutable.d.ts',
'node_modules/immutable/dist/immutable.es.js',
'node_modules/immutable/dist/immutable.js',
'node_modules/immutable/dist/immutable.js.flow',
'node_modules/immutable/dist/immutable.min.js',
“node_modules/immutable/package.json”,
'package.json',
“纱线,锁”
]
有关递归列出目录的dir
程序、查找文件的search
程序,以及更多信息,请参见本节
这是一个预期的日志,还是应该在“这应该在上面的“结果”之后触发”行被记录之前解决并记录所有承诺
是的,这是意料之中的。
当承诺
并非用于同步时,您编写的代码就好像是同步的。
下面的代码片段中实际发生的是在newDirectory
Promise解析之后,getAllFiles
和console.log('这应该在上面的“结果”之后触发)')代码>功能将立即执行。也就是说,console.log在执行之前不会等待getAllFiles
解析
let newDirectory = /// helper function that returns promise of mkdirSync(exampleDir)
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
console.log(result);
});
console.log('this should fire after the "result" from above');
//// rest of promises in promise chain
});
因此,如果您想更改console.log的顺序,以确保它在getAllFiles
Promise解析后执行,您可以按如下方式重写
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
console.log(result);
// or you could add it here
}).then(() => {
console.log('this should fire after the "result" from above');
});
});
您还应该注意到,我所说的是当承诺解决时,而不是当函数完成执行时。有一个非常重要的区别。
如果我们再次以上面的示例为例,并说我们希望在复制完所有文件后执行其他操作
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
...
});
}).then(() => {
console.log('doing some other task');
});
在上面的示例中,将发生的是解析newDirectory
承诺,然后调用getAllFiles
,并且在getAllFiles
完成执行之前,将记录最终的console.log。这是承诺的一个重要原则,如果您希望它们同步运行,您需要链接承诺,即您需要通过所有链接的then函数返回承诺。因此,要解决上述问题,我们需要返回通过getAllFiles
函数解决的承诺,如下所示
newDirectory.then(function(result){
return getAllFiles('/TestDir').then((result) => {
...
});
}).then(() => {
console.log('doing some other task');
});
基于我对node的理解。第二个日志的第一位代码将按顺序出现。这意味着您首先得到承诺,然后->触发承诺链->日志->最终解析getAllFiles链中的所有承诺。您能在代码段中使用更清晰的缩进吗?这将使您的问题更容易回答。感谢您的编辑,但在getAllFiles
中,您的第三个代码段中的括号似乎仍有一些不匹配的地方,请避免!(假设代码实际上是嵌套工作的,不要过早返回并将承诺保留。所有位都作为死代码)