I am trying to achieve multiple async calls and continue only when all are finished.
The process is like this and I need it done in this order: Get a list of file paths, Copy them to new destination, Convert bitmaps, Create thumbnails, Add information into an Array to bulk insert into DB
For this I am using arrays of promises and the Promise.all() method. This method fires too early, because it gets executed when the promise arrays aren't (fully) populated yet. If I delay for some time after the for loop it works, but the time needed depends of the number of files.
Here is the main call:
queueFiles(files).then(function () {
// Add to DB
console.log(current_import.sql.length); // Number of all datasets (0 at runtime)
console.log(current_import.sql); // An array of datasets ([] at runtime)
});
This is the function which does not work as expected:
let import_promises = {
copy_promises : [],
convert_promises : [],
thumbnail_promises : [],
sql_promises : []
};
function queueFiles(files) {
return new Promise(function (resolve, reject) {
try {
for (file of files) {
let original_filename = file.split("\\").pop();
let original_extension = original_filename.split(".").pop();
let hashed_name = crypto.createHmac('sha256', secret).update(original_filename).digest('hex') + "." + original_extension;
let new_path = data_folder + "/" + hashed_name;
import_promises.copy_promises.push(fs.copy(file, new_path).then(function () {
if (original_extension == "bmp") {
import_promises.convert_promises.push(convertFile(new_path).then(function (result) {
import_promises.thumbnail_promises.push(createThumbnail(result.path, thumb_folder + "/" + result.path.split("/").pop(), 500).then(function () {
import_promises.sql_promises.push(addSql(result.data));
}));
}));
} else {
import_promises.thumbnail_promises.push(createThumbnail(new_path, thumb_folder + "/" + hashed_name, 500).then(function () {
import_promises.sql_promises.push(addSql(new_path));
}));
}
}));
}
Promise.all([import_promises.copy_promises, import_promises.convert_promises, import_promises.thumbnail_promises, import_promises.sql_promises])
.then(() => {
// All files copied, converted, thumbnails created and prepared to insert into DB
resolve();
});
}
catch (err) {
reject(err);
}
});
}
And these are the helper functions:
function createThumbnail(input, output, width) {
return new Promise(function (resolve, reject) {
try {
gm(input)
.scale(width)
.write(output, function (err) {
if (err) {
throw(err);
}
resolve();
});
}
catch (err) {
reject(err);
}
});
}
function convertFile(newPath) {
return new Promise(function (resolve, reject) {
try {
let convertedPath = newPath.replace(/\.[^/.]+$/, "") + ".png";
execFile("convert", [newPath, convertedPath]).then(function () {
fs.unlinkSync(newPath);
resolve({path: convertedPath});
});
}
catch (err) {
reject(err);
}
});
}
function addSql(data) {
return new Promise(function (resolve, reject) {
try {
current_import.sql.push(data);
current_import.done++;
updateProgressBar();
resolve();
}
catch (err) {
reject(err);
}
});
}
Edited Code (Working):
function createThumbnail(input, output, width) {
return new Promise(function (resolve, reject) {
gm(input)
.scale(width)
.write(output, function (err) {
if (err) {
return reject(err);
}
resolve();
});
});
}
function convertFile(newPath) {
let convertedPath = newPath.replace(/\.[^/.]+$/, "") + ".png";
return execFile("convert", [newPath, convertedPath]).then(function () {
fs.unlinkSync(newPath);
return convertedPath;
});
}
function addSql(data) {
current_import.sql.push(data);
current_import.done++;
updateProgressBar();
}
function queueFiles(files) {
return Promise.all(files.map(function (file) {
let original_filename = file.split("\\").pop();
let original_extension = original_filename.split(".").pop();
let hashed_name = crypto.createHmac('sha256', secret).update(original_filename).digest('hex') + "." + original_extension;
let new_path = data_folder + "/" + hashed_name;
return fs.copy(file, new_path).then(function () {
if (original_extension == "bmp") {
return convertFile(new_path)
.then(function (path) {
return {path: path, hash: path.split("/").pop()};
});
} else {
return {path: new_path, hash: hashed_name}
}
}).then(function (result) {
let outPath = thumb_folder + "/" + result.hash;
return createThumbnail(result.path, outPath, 500)
.then(function () {
return outPath;
});
}).then(function (thumb_path) {
return addSql(thumb_path);
});
}));
}