Как дождаться ответа всех асинхронных fs.rename, не блокируя процесс? - PullRequest
0 голосов
/ 13 июня 2019

Я создал приложение Angular / Node, которое переименовывает файлы в сетевых папках. Число файлов, которые он переименовывает, составляет от 300 до 500. Я использую await, поэтому я получаю уведомление, когда переименование сделано. Это занимает 8-10 минут на прогон и не может переименовываться одновременно, так как я использую await.

Мне нужно передать количество переименованных файлов и показать пользователю, что переименование уже завершено. Если я не использую async / await, как мой угловой интерфейс узнает, что переименование завершено?

Мой полный код здесь: https://github.com/ericute/renamer

Вот где у меня проблемы с:

    await walk(folderPath, function(err, results) {

        if (err) throw err;            

        results.forEach(file => {

            if (fs.lstatSync(file).isFile) {
                fileCounter++;
            }

            let fileBasename = path.basename(file);
            let filePath = path.dirname(file);

            if (!filesForRenaming[path.basename(file)]) {
                //In a javascript forEach loop,
                //return is the equivalent of continue
                //https://stackoverflow.com/questions/31399411/go-to-next-iteration-in-javascript-foreach-loop
                return;
            }

            let description = filesForRenaming[path.basename(file)].description;

            // Process instances where the absolute file name exceeds 255 characters.
            let tempNewName = path.resolve(filePath, description + "_" + fileBasename);            
            let tempNewNameLength = tempNewName.length;
            let newName = '';            

            if (tempNewNameLength > 255) {
                let excess = 254 - tempNewNameLength;
                if (description.length > Math.abs(excess)) {
                    description = description.substring(0, (description.length - Math.abs(excess)));
                }
                newName = path.resolve(filePath, description + "_" + fileBasename);                
            } else {
                newName = tempNewName;
            }

            renamedFiles++;
            // Actual File Renaming
            fs.renameSync(file, newName, (err) => {
                if (err) { 
                    errList.push(err);
                }
                renamedFiles++;
            });

        });

        if (Object.keys(errList).length > 0) {
            res.send({"status":"error", "errors": errList});
        } else {
            res.send({
                "status":"success",
                "filesFoundInDocData": Object.keys(filesForRenaming).length,
                "filesFound": fileCounter,
                "renamedFiles": renamedFiles,
                "startDate": startDate
            });
        }
    });

1 Ответ

0 голосов
/ 16 июня 2019

Если вы используете какие-либо методы синхронизации, вы в основном блокируете цикл обработки событий.Вы должны изменить всю структуру вашего кода и начать использовать обещания везде.Вы должны быть в состоянии создать еще один сервис на angular, который проверяет, завершен ли процесс переименования, используя timeInterval и GET-запросы (самый простой способ).Например, вы можете установить angular для выборки данных из "/ isRenameCompleted" и предупредить пользователя, если результат равен true или что-то еще.Для получения результатов в режиме реального времени вы должны переключиться на Socket-Io.Быстрое решение для 1 клиента (потому что вам нужно хранить уникальные идентификаторы для каждого запроса и соответственно получать обещания) может быть таким:

1: создайте две глобальные переменные поверх вашего кода

var filesStatus="waiting"
var pendingFiles=[]

2: Внутри логического маршрута переименования нажмите каждый файл в массиве обещаний, используя цикл for, и начните асинхронно ждать завершения процесса переименования

pendingFiles.push(fsPromises.rename(oldName,newName))
Promise.all(pendingFiles)
  .then(values => { 
    filesStatus = "done"
  })
  .catch(error => { 
    filesStatus = "error"
  });
filesStatus="pending"

3: Теперь добавьтеновый маршрут /isRenameCompleted, который будет иметь логику отчета, подобную следующей

router.get('/isRenameCompleted', (req, res, next) => {
 if (filesStatus==="pending"){
    res.end("please wait")
 } else if (filesStatus==="done"){
    res.end("done! your files renamed")   
 }
}
...