Javascript 递归爬网并以异步方式写入文件
我对异步代码是完全陌生的,所以我现在有点不知所措 我所做的是递归地异步抓取归档文件,以便检测给定归档文件中的文件路径。我要做的是,当检测到所有文件路径时,将它们写入单个文件。但是,当我执行代码时,它不会正确地将它们写入文件。我假设这是由于多个写操作同时发生 data.json之前 data.json之后 显然,我可以同步编写代码,但为了提高效率,我更愿意异步编写所有这些代码。尽管如此,我真的不确定最好的方法。我知道这样做的一种方法是等待最后一个文件被爬网和推送(然后将新属性写入文件),但我不知道如何在异步环境中有效地检测它?我可以经常检查,但在我看来这是一个愚蠢的方法 下面是导致问题的异步代码Javascript 递归爬网并以异步方式写入文件,javascript,node.js,asynchronous,async-await,fs,Javascript,Node.js,Asynchronous,Async Await,Fs,我对异步代码是完全陌生的,所以我现在有点不知所措 我所做的是递归地异步抓取归档文件,以便检测给定归档文件中的文件路径。我要做的是,当检测到所有文件路径时,将它们写入单个文件。但是,当我执行代码时,它不会正确地将它们写入文件。我假设这是由于多个写操作同时发生 data.json之前 data.json之后 显然,我可以同步编写代码,但为了提高效率,我更愿意异步编写所有这些代码。尽管如此,我真的不确定最好的方法。我知道这样做的一种方法是等待最后一个文件被爬网和推送(然后将新属性写入文件),但我不知道
// Scan directories looking for target file types.
async function scanDirs(){
const
config = await fsp.readFile('./config.json', 'utf8'),
archives = JSON.parse(config).archives,
{ join } = require('path'),
traverse = async (path) => {
try {
const stats = await fsp.stat(path)
if (stats.isDirectory()){
const childPaths = await fsp.readdir(path)
for (const childPath of childPaths){
const
fullPath = join(path, childPath)
traverse(fullPath)
}
} else if (stats.isFile()) {
const
fileTypes = config.fileTypes,
fileExt = path.substring(path.lastIndexOf('.')+1)
if (fileTypes.includes(fileExt)){
const
data = await fsp.readFile('./data.json', 'utf8'),
json = JSON.parse(data),
drive = path.substring(0,1),
files = json[drive].files,
stat = await fsp.stat(path),
newFile = {
"path": path,
"name": path.substring(path.lastIndexOf('\\')+1),
"bytes": stat.size
}
files.push(newFile)
fsp.writeFile('./data.json', JSON.stringify(json, null, 2))
}
}
}
catch (error){
console.error(error)
}
}
for (const path of archives){
traverse(path)
}
}
任何帮助都将不胜感激
我知道这样做的一种方法是等到最后一个文件被爬网后再写,但我不知道如何在异步环境中有效地检测它
您将使用等待多个承诺:
const { join } = require('path');
async function searchFiles(path, fileTypes) {
try {
const stats = await fsp.stat(path)
if (stats.isDirectory()){
const childPaths = await fsp.readdir(path)
const promises = childPaths.map(childPath =>
searchFiles(join(path, childPath), fileTypes)
);
const results = await Promise.all(promises);
return [].concat(...results);
} else if (stats.isFile()) {
const fileExt = path.substring(path.lastIndexOf('.')+1)
if (fileTypes.includes(fileExt)) {
return [{
"path": path,
"name": path.substring(path.lastIndexOf('\\')+1),
"bytes": stats.size
}];
}
}
} catch(e) {
// ignore. Log?
}
return [];
}
async function readJson(path) {
return JSON.parse(await fsp.readFile(path, 'utf8'));
}
// Scan directories looking for target file types.
async function scanDirs() {
try {
const [config, data] = await Promise.all([readJson('./config.json'), readJson('./data.json')]);
const results = await Promise.all(config.archives.map(path => searchFiles(path, config.fileTypes)));
for (const newFile of [].concat(...results)) {
const drive = newFile.path.substring(0,1);
data[drive].files.push(newFile);
}
fsp.writeFile('./data.json', JSON.stringify(data, null, 2));
} catch (error){
console.error(error)
}
}
BTW,您可能需要考虑从字符串操作中使用<代码> BaseNe< /COD>和<代码> ExtNeX/COD>,但鉴于这只是一个Windows程序(使用驱动器字母),这可能没什么关系。
在您的函数中递归地构建数据结构,将其作为一个对象(作为一个允诺)返回。然后等待,序列化它,只将它写入文件一次。你说的“我不关心结构”是什么意思?肯定是乱码输出不好吗?@Bergi所有im记录的都是我粘贴到文件中的文件路径array@Bergi我很困惑如何返回遍历作为承诺。im从for
循环调用遍历
的次数不定,而循环本身调用的次数不定。我对承诺和异步代码是全新的,所以请像我5岁时那样给我解释一下,我现在就要玩这个,这样我就可以理解发生了什么。感谢您的帮助所以基本上任何异步函数都是一个承诺,对吗?另外,当我从另一个函数调用readJson('path')
时,在新上下文中readJson
是一个等待的承诺吗?例如,async someOtherFunction(){const c=readJson('./config.json'),d=c.archives}
,d
将始终在此上下文中等待c
,或者无论上下文是什么?@PrimitiveNom Everyasync
异步函数都会返回一个承诺,是。@PrimitiveNom上下文不会等待,直到您显式地等待承诺。不,示例中的d
不会等待c
,它不知道或不关心readJson
做什么。“等待上下文”只是当前的异步函数
,它的执行被暂停(就像生成器一样),直到当前等待的承诺得到解决。其他函数中的其他代码不受此影响。
// Scan directories looking for target file types.
async function scanDirs(){
const
config = await fsp.readFile('./config.json', 'utf8'),
archives = JSON.parse(config).archives,
{ join } = require('path'),
traverse = async (path) => {
try {
const stats = await fsp.stat(path)
if (stats.isDirectory()){
const childPaths = await fsp.readdir(path)
for (const childPath of childPaths){
const
fullPath = join(path, childPath)
traverse(fullPath)
}
} else if (stats.isFile()) {
const
fileTypes = config.fileTypes,
fileExt = path.substring(path.lastIndexOf('.')+1)
if (fileTypes.includes(fileExt)){
const
data = await fsp.readFile('./data.json', 'utf8'),
json = JSON.parse(data),
drive = path.substring(0,1),
files = json[drive].files,
stat = await fsp.stat(path),
newFile = {
"path": path,
"name": path.substring(path.lastIndexOf('\\')+1),
"bytes": stat.size
}
files.push(newFile)
fsp.writeFile('./data.json', JSON.stringify(json, null, 2))
}
}
}
catch (error){
console.error(error)
}
}
for (const path of archives){
traverse(path)
}
}
const { join } = require('path');
async function searchFiles(path, fileTypes) {
try {
const stats = await fsp.stat(path)
if (stats.isDirectory()){
const childPaths = await fsp.readdir(path)
const promises = childPaths.map(childPath =>
searchFiles(join(path, childPath), fileTypes)
);
const results = await Promise.all(promises);
return [].concat(...results);
} else if (stats.isFile()) {
const fileExt = path.substring(path.lastIndexOf('.')+1)
if (fileTypes.includes(fileExt)) {
return [{
"path": path,
"name": path.substring(path.lastIndexOf('\\')+1),
"bytes": stats.size
}];
}
}
} catch(e) {
// ignore. Log?
}
return [];
}
async function readJson(path) {
return JSON.parse(await fsp.readFile(path, 'utf8'));
}
// Scan directories looking for target file types.
async function scanDirs() {
try {
const [config, data] = await Promise.all([readJson('./config.json'), readJson('./data.json')]);
const results = await Promise.all(config.archives.map(path => searchFiles(path, config.fileTypes)));
for (const newFile of [].concat(...results)) {
const drive = newFile.path.substring(0,1);
data[drive].files.push(newFile);
}
fsp.writeFile('./data.json', JSON.stringify(data, null, 2));
} catch (error){
console.error(error)
}
}