javascriptnode.jsasynchronousfsfs-extra

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.


Solution

  • 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);
        }
    })();