[英]Node.js return a promise in recursive loop
I am having trouble getting a loop to return nested promises in the correct order.我无法让循环以正确的顺序返回嵌套的 Promise。 I am trying to recursively loop through a directory with subdirectories to get files and copy them to a new directory.
我试图递归地遍历一个包含子目录的目录以获取文件并将它们复制到一个新目录。 This functionality is happening inside a Promise chain since other processes need to complete in order after the files are copied.
此功能发生在 Promise 链中,因为其他进程需要在复制文件后按顺序完成。
The following code works but when I console.log out a string after the .then()
it is logging before the promise has resolved.下面的代码有效,但是当我在
.then()
之后 console.log out 一个字符串时.then()
它在承诺解决之前记录。
The code is:代码是:
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
});
The recursive function I'm calling "getAllFiles" to go through "TestDir" and its sub folders and copy files to "ExampleDir"我正在调用“getAllFiles”的递归函数通过“TestDir”及其子文件夹并将文件复制到“ExampleDir”
const getAllFiles = function(dirPath, arrayOfFiles) {
return new Promise((resolve, reject) => {
var promises = [];
files = fs.readdirSync(dirPath)
arrayOfFiles = arrayOfFiles || []
files.forEach(function(file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = resolve(getAllFiles(dirPath + "/" + file, arrayOfFiles));
} else {
promises.push(helper.copyFile(path.join(dirPath, "/", file),`/TestDir/${file}`))
}
}
})
Promise.all(promises).then(function(result){
resolve(result);
console.log('done with Promises');
}).catch((error) => {
reject(error);
});
}
The helper that copies the files returns a promise after the file has been copied复制文件的助手在复制文件后返回一个承诺
exports.copyFile = function(file, destDir){
return new Promise(function ( resolve, reject ){
fs.copyFile(file, destDir, (err) => {
if(err) reject( err );
else resolve('Successfully copied');
});
});
}
This actually seems to work but I am concerned it wont work with a large set of data since the这实际上似乎有效,但我担心它不适用于大量数据,因为
console.log('this should fire after the "result" from above');
fires before the other logs.在其他日志之前触发。 The console looks like:
控制台看起来像:
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
Is this a log to be expected, or should all of the promises resolve and be logged before the "'this should fire after the "result" from above'" line is logged?这是一个预期的日志,还是应该在记录“'这应该在上面的“结果”之后触发'”行之前解决并记录所有承诺?
Is this a log to be expected, or should all of the promises resolve and be logged before the "'this should fire after the "result" from above'" line is logged?
这是一个预期的日志,还是应该在记录“'这应该在上面的“结果”之后触发'”行之前解决并记录所有承诺?
Yes, it is to be expected.是的,这是可以预料的。 You are writing your code as if it is synchronous when
Promises
are not meant for that.当
Promises
不适用时,您编写的代码就好像它是同步的一样。 What is actually happening in the code snippet below is after the newDirectory
Promise resolves, both the getAllFiles
and console.log('this should fire after the "result" from above');
下面的代码片段中实际发生的事情是在
newDirectory
Promise 解析之后, getAllFiles
和console.log('this should fire after the "result" from above');
getAllFiles
console.log('this should fire after the "result" from above');
function will be executed immediately.函数将立即执行。 That is, the console.log will not wait on the
getAllFiles
to resolve before executing.也就是说,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
});
So if you wanted to change the order of the console.log to ensure that it is executed after the getAllFiles
Promise resolves, you could rewrite as follows.所以如果你想改变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');
});
});
You should also notice that I am saying when the Promise resolves, not when the function has finished executing.您还应该注意到,我说的是 Promise 何时解析,而不是函数完成执行时。 There is a very important distinction.
有一个非常重要的区别。 If we take your example above yet again and and say we wanted to perform some other action after all the files had been copied.
如果我们再次以上面的示例为例,并说我们想在复制所有文件后执行一些其他操作。
newDirectory.then(function(result){
getAllFiles('/TestDir').then((result) => {
...
});
}).then(() => {
console.log('doing some other task');
});
In the above example, what would happen is the newDirectory
Promise would resolve, then the getAllFiles
would be invoked, and before the getAllFiles
has finished executing, the final console.log would be logged.在上面的例子中,会发生什么是
newDirectory
Promise 将解析,然后getAllFiles
将被调用,并且在getAllFiles
完成执行之前,最终的 console.log 将被记录。 This is an important principal of Promises, if you wish them to behave synchronously, you need to chain the Promises, ie you need to return the promise through all the chained then functions.这是 Promises 的一个重要原则,如果你希望它们同步运行,你需要链接 Promises,即你需要通过所有链接的 then 函数返回承诺。 So to fix the above problem we would need to return the promise that is resolved from the
getAllFiles
function as follows因此,要解决上述问题,我们需要返回从
getAllFiles
函数解析的承诺,如下所示
newDirectory.then(function(result){
return getAllFiles('/TestDir').then((result) => {
...
});
}).then(() => {
console.log('doing some other task');
});
fs/promises and fs.Dirent fs/promises 和 fs.Dirent
Here's an efficient, non-blocking ls
program using Node's fast fs.Dirent objects and fs/promises module.这是一个使用 Node 的快速fs.Dirent对象和fs/promises模块的高效、非阻塞
ls
程序。 This approach allows you to skip wasteful fs.exist
or fs.stat
calls on every path.这种方法允许您跳过每条路径上浪费的
fs.exist
或fs.stat
调用。
Using async
and await
, we can avoid having to think much about how to specifically wire up the Promises -使用
async
和await
,我们可以避免过多考虑如何专门连接 Promises -
// 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)
Let's get some sample files so we can see ls
working -让我们获取一些示例文件,以便我们可以看到
ls
工作 -
$ yarn add immutable # (just some example package)
$ node main.js
[
'.',
'main.js',
'node_modules',
'node_modules/.yarn-integrity',
'node_modules/immutable',
'node_modules/immutable/LICENSE',
'node_modules/immutable/README.md',
'node_modules/immutable/contrib',
'node_modules/immutable/contrib/cursor',
'node_modules/immutable/contrib/cursor/README.md',
'node_modules/immutable/contrib/cursor/__tests__',
'node_modules/immutable/contrib/cursor/__tests__/Cursor.ts.skip',
'node_modules/immutable/contrib/cursor/index.d.ts',
'node_modules/immutable/contrib/cursor/index.js',
'node_modules/immutable/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',
'yarn.lock'
]
See this related Q&A for a dir
program that recursively lists directories, a search
program for finding files, and more.有关递归列出目录的
dir
程序、用于查找文件的search
程序等,请参阅此相关问答。
If you want to console log after a Promise
, you'll have to do it in a .then()
like如果你想在
Promise
之后控制台日志,你必须在.then()
做
Promise.all(promises).then(function(result) {
resolve(result);
console.log('done with Promises');
})
.then(() => console.log("this should fire after the "result" from above"))
.catch((error) => reject(error));
This is because Promise
s are non-blocking and anything after it won't wait for it to finish before executing.这是因为
Promise
是非阻塞的,它之后的任何东西都不会等待它完成后再执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.