Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rename files asynchronously in Node.js if destination files don't exist

I am trying to rename files asynchronously in Node.js only if destination files don't exist.

I made a quick test like follows:

const fs = require('fs')

const files = [ 'file1', 'file2', 'file3' ]
const new_name = 'new-name' // same destination name for all 

fs.exists() - DEPRECATED

for (let file of files)

  fs.exists(new_name, (exists) => {
    if (!exists) fs.rename(file, new_name, (err) => {})
   })

fs.access() - RECOMMENDED

for (let file of files)

  fs.access(new_name, fs.constants.F_OK, (err) => {
    if (err) fs.rename(file, new_name, (err) => {})
  })

fs.move() - from fs-extra

const fs_extra = require('fs-extra')

for (let file of files) 

  fs_extra.move(file, new_name, { overwrite: false }, (err) => {})

Each time all 3 files were overwriten and renamed to one file.


I believe this is happens because all exists checks fire sooner than any rename happens.

I know how to accomplish this task synchronously, but want to be sure that there is no proper async way to do so.

like image 818
Systems Rebooter Avatar asked Dec 22 '25 04:12

Systems Rebooter


2 Answers

You can create Promise which resolve's when file is renamed

fs.rename(file, new_name, (err) => {
    resolve(); <------
});

or when renaming is skipped

fs.access(new_name, fs.constants.F_OK, (err) => {
    if (err) {
        return fs.rename(file, new_name, (err) => {
            resolve();
        });
    }
    resolve(); <------
});

Full code

(async () => {
    for (let file of files) {
        await new Promise((resolve) => {
            fs.access(new_name, fs.constants.F_OK, (err) => {
                if (err) {
                    return fs.rename(file, new_name, (err) => {
                        resolve();
                    });
                }
                resolve();
            });
        });
    }
})();

and if you don't want to mix async/await with Promise

(async () => {
    function rename(file, new_name) {
        return new Promise((resolve) => {
            fs.access(new_name, fs.constants.F_OK, (err) => {
                if (err) {
                    return fs.rename(file, new_name, (err) => {
                        resolve();
                    });
                }
                resolve();
            });
        });
    }

    for (let file of files) {
        await rename(file, new_name);
    }
})();
like image 166
ponury-kostek Avatar answered Dec 23 '25 17:12

ponury-kostek


@ponury-kostek solution works brilliantly and marked as accepted answer.

I ended up with the following code since it's a bit shorter:

async function rename_files() {

  for (let file of files)                
      await fs_extra.move(file, new_name)
}
rename_files()  
like image 30
Systems Rebooter Avatar answered Dec 23 '25 18:12

Systems Rebooter