Question

The task is the following. I need to delete folder and it's content using rmdir from fs2 library. It returns deffered promise object. After it complete I need to create folder again. And function should return promise as well for further chaining.

The code I have right now is like this:

function clearOutputDir() {
    var def = deferred();
    var def2 = deferred();

    if (fs.existsSync(outputFolder)) {
        def.resolve(fs2.rmdir(outputFolder, {recursive: true, force: true}));
    } else {
        def.resolve();
    }

    def2.resolve(def.promise.then(function () {
        return deferred.promisify(fs.mkdir)(outputFolder);
    }));

    return def2.promise;
}

Looks like it works, but it's ugly as hell. Can it be done simpler without def and def2?

Was it helpful?

Solution

Firstly, there's no need to create unresolved promises on your own, it's cleaner to work with those returned by fs2 functions and "then" them, Plynx solution is good hint in that case

Secondly, due to async nature of things (and of how file system functions internally works), you should not use fs.exists function, it's generally deprecated, and good idea is to not use it at all, but do what you want to do directly, and rely on eventual error codes. See: https://groups.google.com/forum/?fromgroups=#!topic/nodejs/gRRuly79oRc it sheds more light on this.

Getting to the solution: Instead of promisifing fs.mkdir, you can also use mkdir from fs2, as unless custom options are supplied it works exactly as (fallbacks to) fs.mkdir, and does what you want, returns promise.

function clearOutputDir(outputFolder) {
    return fs2.rmdir(outputFolder, { recursive: true, force: true }).then(null, function (e) {
         // Ignore "No such dir" error, otherwise propagate further
         if (e.code === 'ENOENT') return null;
         throw e;
     }).then(fs2.mkdir.bind(fs2, outputFolder));
}

fs2.mkdir will be run only if fs2.rmdir is succesful or dir didn't exist. I've used bind to pass it with outputFolder, it has same effect as:

function clearOutuputDir(outputFolder) {
    ...
    }).then(function () { return fs2.mkdir(outputFolder); });
}

If you decide to use promisify, it's always better to reuse promisified version, and do not create it on each function call:

var mkdir = deferred.promisify(fs.mkdir);
function clearOutputDir(outputFolder) {
    ...
   }).then(function () { return mkdir(outputFolder); });
}

OTHER TIPS

Consider using final-fs library for this purpose. Take a look at it's chaining example - it's very clean.

It uses when library for it's async calls so it's almost impossible to fall into callback hell. Chaining is very easy - it's done by returning a Promise in a resolve function.

The code would look like that:

var ffs = require('final-fs');

/**
 * @param {string} outputDir
 * @returns {Promise}
 */
function clearOutputDir(outputDir) {
    return ffs.exists(outputDir)
        .then(function (exists) {
            if (exists) {
                return ffs.rmdirRecursive(outputDir);
            }
        })
        .then(function () {
            return ffs.mkdir(outputDir);
        });
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top