Как пройти через дерево каталогов параллельно? - PullRequest
0 голосов
/ 04 мая 2020

Сначала я хочу описать проблему: у меня есть дерево каталогов (глубина = 3), которое содержит несколько каталогов и файлов. Некоторые из этих файлов имеют расширение .txt, а некоторые .mp4. Я хочу копировать только файлы .mp4 в новый каталог с той же иерархией, что и в исходном каталоге (другими словами, я не хочу копировать все файлы mp4 в одну папку, я хочу скопировать все каталоги как есть, а затем скопируйте mp4 файлы). Вопрос: как копировать эти файлы не последовательно, а параллельно? Вот мой код:

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');

const sourceDir = process.argv[2];
const stat = promisify(fs.stat);
const copy = promisify(fs.copyFile);
const mkdir = promisify(fs.mkdir);
const readdir = promisify(fs.readdir);
const targetDir = path.join(__dirname, 'only-mp4');

// creating root folder, all files will be copied here
(async () => {
  await mkdir(targetDir);
})();

const copyMediaFiles = async (node) => {
  try {
    const stats = await stat(node);
    if (stats.isDirectory()) {
      let children = await readdir(node);
      // constructing new paths
      children = children.map((child) => path.join(node, child));
      // "copying" file hierarchy (basically just recreating same file hierarchy in target directory)
      children.forEach((child) => {
        const courseDirs = child.split('/').slice(4, 7).join('/');
        mkdir(path.join(targetDir, courseDirs), { recursive: true });
      });
      // running this function for all children recursively in parallel
      const promises = children.map(copyMediaFiles);
      await Promise.all(promises);
    }
    const ext = path.extname(node);
    const filename = path.basename(node);
    // if file extension == mp4 then copy that file in target directory
    if (ext === '.mp4') {
      await copy(
        node,
        path.join(
          targetDir,
          path.dirname(node).split('/').slice(4).join('/'),
          filename
        )
      );
      console.log('File copied: ', filename);
    }
    return;
  } catch (error) {
    console.log(error);
  }
};

copyMediaFiles(sourceDir).then(() => console.log('All mp4 files copied'));

Да, это работает, но я не уверен, что все сделал правильно. Любой посоветуете что? Что я тут не так сделал? И я не уверен, что правильно обхожу это дерево.

1 Ответ

1 голос
/ 05 мая 2020

Две проблемы:

  • Первый вызов copyMediaFiles произойдет до разрешения первого обещания mkdir. Это рискованно, так как вы можете попытаться получить доступ к целевому каталогу до его создания. Если вы просто поместите вызов copyMediaFiles в async IIFE, то у вас нет такого риска:

    (async () => {
        await mkdir(targetDir);
        await copyMediaFiles(sourceDir);
        console.log('All mp4 files copied');
    })();
    
  • Второй вызов mkdir сделанный без захвата обещания, которое он возвращает, таким образом, там также возникает подобный риск.

Возможное улучшение, чтобы "хрустнуть" немного больше:

Ваша цель состоит в том, чтобы свести к минимуму JavaScript время простоя (в ожидании разрешения обещаний), и это можно сделать, увеличив максимальное число ожидающих обещаний.

По этой причине было бы лучше инициировать вызов на copyMediaFiles сразу после разрешения соответствующего 10 * * обещания, а не начинать создание всех дочерних папок перед тем, как сделать этот вызов:

const children = await readdir(node);
const promises = children.map(async child => {
    child = path.join(node, child);
    const courseDirs = child.split('/').slice(4, 7).join('/');
    await mkdir(path.join(targetDir, courseDirs), { recursive: true });
    await copyMediaFiles(child);
});
await Promise.all(promises);

С помощью этого кода вы потенциально можете начать вызов copyMediaFiles перед всеми братьями и сестрами каталоги были созданы. Если у ваших каталогов высокий коэффициент ветвления, это означает, что вы получите более длинный список ожидающих обещаний, который может быть полезен для общей производительности.

Все зависит от того, насколько хорошо базовый API управляет параллелизмом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...