The goal: given a file path and an array of file paths in input, I need to check if there is a file in the array (even with a different name) that is equal to the input file. I need to read the files asynchronously and stop if an equal file is found (in that case I don't want to read all the files in the array).
I would like to use only ecma6 features without additional libraries. I am using node.js, thus to compare the files is sufficient to use the compare buffers function:
const fs = require("fs");
let buffer1 = fs.readFileSync(filePath1);
let buffer2 = fs.readFileSync(filePath2);
if (buffer1.compare(buffer2) === 0) {
console.log("Files are equal");
} else {
console.log("Files are different");
}
Basically for each of the file path in the array I want to check equality sequentially (but with asynchronous read) with a function like this:
function isFileEqual (fileInputBuffer, path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) {
resolve(false);
}
if (data.compare(fileInputBuffer) === 0) {
resolve(true);
} else {
resolve(false);
}
});
});
}
I came up with a solution taking advantage of the "reject fast" property of Promises.all, which cause to reject immediately in case any of the input promises reject without wait for the other promises to complete:
const fs = require("fs");
let paths = ["file2", "file3", "file1_copy", "file4", "file5"];
checkIfFileAlreadyExistsAsync("file1", paths).then(path => {
console.log("\n", "File equal found at: ", path, "\n");
}).catch(err => {
console.log(err);
});
function checkIfFileAlreadyExistsAsync(filePath, paths) {
return new Promise( (rootResolve, rootReject) => {
fs.readFile(filePath, (err, inputBuffer) => {
if (err) {
rootReject(err);
return;
}
function isFileEqual(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
console.log("[isFileEqual]", path);
if (err) {
resolve();
}
else if (data.compare(inputBuffer) === 0) {
reject(path); // file equal found, reject fast!
} else {
resolve();
}
});
});
}
let promises = [];
// fill promises array
paths.forEach(path => {
promises.push(isFileEqual(path));
})
Promise.all(promises).then(values => {
rootReject(false);
})
.catch((path) => {
// use reject fast to resolve without wait the other promises to complete
rootResolve(path);
});
});
});
}
Output of the above script:
[isFileEqual] file2
[isFileEqual] file1_copy
File equal found at: file1_copy
[isFileEqual] file4
[isFileEqual] file3
[isFileEqual] file5
The above solution works but as you can see there is a problem: all the files are always read regardless if an equal file has already been found.
I didn't know before, but a Promise is executed as soon as it is created (I though the opposite, why was it implemented in this way?), thus I was thinking to use a promise factory like the following:
function promiseFactory(path) {
return function () {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
console.log("[isFileEqual]", path);
if (err) {
reject();
}
else if (data.compare(inputBuffer) === 0) {
resolve(true);
} else {
resolve(false);
}
});
});
};
}
and try to run the promises in sequence. But how can I do that? Or are there alternative ways?