Javascript Node.js异步查找具有相同内容的文件
目标:给定输入中的文件路径和文件路径数组,我需要检查数组中是否有与输入文件相同的文件(即使名称不同)。我需要异步读取文件,如果找到相同的文件,则停止读取(在这种情况下,我不希望读取数组中的所有文件) 我只想使用ecma6功能,不需要额外的库。 我使用的是node.js,因此比较文件就足以使用: 基本上,对于数组中的每个文件路径,我希望使用如下函数按顺序检查相等性(但使用异步读取):Javascript Node.js异步查找具有相同内容的文件,javascript,node.js,file,es6-promise,fs,Javascript,Node.js,File,Es6 Promise,Fs,目标:给定输入中的文件路径和文件路径数组,我需要检查数组中是否有与输入文件相同的文件(即使名称不同)。我需要异步读取文件,如果找到相同的文件,则停止读取(在这种情况下,我不希望读取数组中的所有文件) 我只想使用ecma6功能,不需要额外的库。 我使用的是node.js,因此比较文件就足以使用: 基本上,对于数组中的每个文件路径,我希望使用如下函数按顺序检查相等性(但使用异步读取): function isFileEqual (fileInputBuffer, path) { return
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);
}
});
});
}
我提出了一个解决方案,利用的“拒绝快速”属性,在任何输入承诺拒绝时立即拒绝,而不等待其他承诺完成:
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);
});
});
});
}
上述脚本的输出:
[isFileEqual] file2
[isFileEqual] file1_copy
File equal found at: file1_copy
[isFileEqual] file4
[isFileEqual] file3
[isFileEqual] file5
上述解决方案有效,但正如您所看到的,存在一个问题:无论是否已找到相同的文件,都将始终读取所有文件
我以前不知道,但是一个承诺一创建就被执行(我想是相反的,为什么它是以这种方式实现的?),因此我想使用如下的承诺工厂:
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);
}
});
});
};
}
试着按顺序履行承诺。但是我怎么能做到呢?或者有其他的方法吗?承诺。种族 Promise.race是一个有趣的函数——它不是等待所有承诺被解析或拒绝,而是在数组中的任何承诺被解析或拒绝时立即触发Promise.race:
var req1 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
console.log('Then: ', one);
}).catch(function(one, two) {
console.log('Catch: ', one);
});
//从控制台:
//然后:第二
检查这一个,我相信它将帮助您我找到了一个使用递归的解决方案。基本上,我创建一个工厂数组(每个工厂返回一个函数,该函数返回一个承诺),然后使用该数组作为递归函数的输入。如果找到一个相等的文件,递归函数将解析主承诺(由主函数返回的承诺),否则它将在工厂数组和要创建的下一个承诺的索引中以递归方式调用自己
const fs = require("fs");
let paths = ["file2", "file3", "file1_copy", "file4", "file5"];
checkIfFileAlreadyExistsAsync("file1", paths).then(result => {
console.log("SUCCESS", result);
}).catch(err => {
console.log("FAIL", err);
});
function checkIfFileAlreadyExistsAsync(filePath, paths) {
return new Promise((rootResolve, rootReject) => {
fs.readFile(filePath, (err, inputBuffer) => {
if (err) {
rootReject(err);
}
function compareFilePromiseFactory(path) {
return function() {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
console.log("Compare file: ", path, "\n");
if (err) {
resolve(false);
}
else if(data.compare(inputBuffer) === 0) {
resolve(true);
}
else {
resolve(false);
}
});
});
}
}
let factories = [];
paths.forEach(path => {
factories.push(compareFilePromiseFactory(path));
});
function findFile(factories, index) {
if (index == factories.length) {
rootReject(false);
return;
}
factories[index]().then(result => {
if (result) {
rootResolve(true);
}
else {
findFile(factories, index + 1);
}
});
}
findFile(factories, 0);
});
});
}
上述脚本提供以下输出:
Compare file: file2
Compare file: file3
Compare file: file1_copy
SUCCESS true
每个承诺都是按顺序创建的,但都是异步的。一旦找到相等的文件,它就会停止生成承诺。
我不知道它是否也可能是一个迭代的解决方案,你认为呢?嗨,谢谢你的回答。不幸的是,Promise.race的工作原理与Promise.all类似,它需要一系列承诺。一旦承诺被创建,它就被执行,你不能阻止它。承诺不是“执行”的。promise只是一个表示异步结果的对象,在调用(执行)异步函数时创建它们(如您的例子中的
fs.readFile
)@Bergi是的,我的意思是,在创建promise后,立即执行由promise包装的函数。感谢您的澄清,这几乎是正确的解决方案。您应该将外部的newpromise
仅包装在fs.readFile
周围,并放置findFile(工厂,0)
在中,然后回调,这样您就可以使用promise了chaining@Bergi您是否打算这样做:返回新承诺((resolve,reject)=>{fs.readFile(..=>{if(err)reject(err);else resolve(inputBuffer);});})。然后(inputBuffer=>{return new Promise((rootResolve,rootReject)=>{/[…]findFile(工厂,0);});});这样做有什么好处吗?我已经返回了一个承诺,因此我可以进行链接:CheckIffileReadyExistsAsync(“文件1”,路径)。然后(…)是的,像这样,但是-findFile(Factorys,0)
已经创建了一个承诺,您可以直接返回。
Compare file: file2
Compare file: file3
Compare file: file1_copy
SUCCESS true