why is objlist being resolved before loop has completed. Since the for loop is sync, I expected resolve to be run after my for loop has completed, but list with empty object is being resolved.
function myFunction(path,files){
return new Promise(function(resolve,reject){
let objlist=[];
files.forEach(function(file,index){
console.log(file)
objlist[index]={};
fs.stat(path + '\\' + file, function(err,stats){
if(err){
console.log(err.code)
console.log(err)
throw err;
}
if(stats.isDirectory()){
objlist[index].file = 'folder'
}
else if(stats.isFile()){
objlist[index].file = 'file'
}
objlist[index].name = file
console.log(objlist[index].name) //gives correct output
objlist[index].size = stats.size
});
})
console.log(objlist); //gives list of empty objects
resolve(objlist);
});
}
forEach
expects a function and executes it synchronously (see JavaScript, Node.js: is Array.forEach asynchronous? ). fs.stat
is async so there's no guarantee that all iterations will finish before calling resolve(objlist)
.
Here's a suggestion:
const objlist = await Promise.all(files.map(async file => {
// return a promise
}))
resolve(objlist)
I like this article on promises and ES6 for a primer:https://developers.google.com/web/fundamentals/primers/promises
Edited thanks to feedback from https://stackoverflow.com/users/6351322/guijob
The fs.stat
call is not synchronous because it uses a callback. Your forEach
loop will launch all of the fs.stat
processes and then continue, but it will not (nor can you make it) wait for all of the callbacks to be called before it carries on.
I think an approach like the one below will work. fs-extra
is a good promisified version of fs
, and bluebird
is a promise library that extends standard Promises. I'm not sure if that is necessary, you didn't say which version of Node you're using; I'm using it to get access to the each
method. Try it without Bluebird, it might work.
var fs = require('fs-extra');
var Promise = require('bluebird');
global.Promise = Promise;
function myFunction(path,files){
return Promise
.each(files, function(file) {
return fs
.stat(path + '\\' + file)
.then(function(stats) {
var result = {
name: file,
size: stats.size
};
if ( stats.isDirectory() ) result.file = 'folder';
if ( stats.isFile() ) result.file = 'file'
return Promise.resolve(result);
});
})
.tap(console.log)
.catch(function(err) {
console.log(err.code);
console.log(err);
throw err;
});
}
If you don't want to use await you can either use Promise.all or a counter to know when all fs.stat
has resolved:
Using only Promise.all
:
function myFunction(path,files){
return Promise.all(files.map(function(file,index){
console.log(file)
var obj = {};
fs.stat(path + '\\' + file, function(err,stats){
if(err){
console.log(err.code)
console.log(err)
throw err;
}
if(stats.isDirectory()){
obj.file = 'folder'
}
else if(stats.isFile()){
obj.file = 'file'
}
obj.name = file
console.log(objlist[index].name) //gives correct output
obj.size = stats.size
return obj;
});
}))
.then(objList => {
console.log(objlist); //gives list of empty objects
resolve(objlist);
});
}
Or using a counter:
function myFunction(path,files){
return new Promise(function(resolve,reject){
let objlist=[];
var counter = files.length;
files.forEach(function(file,index){
console.log(file)
objlist[index]={};
fs.stat(path + '\\' + file, function(err,stats){
counter--;
if(err){
console.log(err.code)
console.log(err)
throw err;
}
if(stats.isDirectory()){
objlist[index].file = 'folder'
}
else if(stats.isFile()){
objlist[index].file = 'file'
}
objlist[index].name = file
console.log(objlist[index].name) //gives correct output
objlist[index].size = stats.size
if(counter == 0) {
console.log(objlist); //gives list of empty objects
resolve(objlist);
}
});
})
});
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.