使用ES6承诺进行递归的好方法?

时间:2015-08-28 16:29:24

标签: javascript es6-promise

以下是我所拥有的:

function nextAvailableFilename(path) {
    return new Promise(function (resolve, reject) {
        FileSystem.exists(path, function (exists) {
            if (!exists) return resolve(path);

            var ext = Path.extname(path);
            var pathWithoutExt = path.slice(0, -ext.length);

            var match = /\d+$/.exec(pathWithoutExt);
            var number = 1;

            if (match) {
                number = parseInt(match[0]);
                pathWithoutExt = pathWithoutExt.slice(0, -match[0].length);
            }

            ++number;

            nextAvailableFilename(pathWithoutExt + number + ext).then(function () {
                return resolve.apply(undefined, arguments);
            }, function () {
                return reject.apply(undefined, arguments);
            });
        });
    });
}

但我最后不喜欢那个街区 - 是不是有办法替换'当前承诺与堆栈中的下一个承诺而不是让一个承诺解决下一个承诺,就像我在这里完成的那样?

2 个答案:

答案 0 :(得分:1)

这是一个使用保证链和文件创建的版本,以避免竞争条件。我使用了bluebird promise库,所以我可以使用fs库中的promises来简化代码和错误处理:

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');

// Creates next available xxx/yyy/foo4.txt numeric sequenced file that does
// not yet exist.  Returns the new filename in the promise
// Calling this function will create a new empty file.
function nextAvailableFilename(filename) {
    return fs.openAsync(filename, "wx+").then(function(fd) {
        return fs.closeAsync(fd).then(function() {
            return filename;
        });
    }, function(err) {
        if (err.code !== 'EEXIST') {
            // unexpected file system error
            // to avoid possible looping forever, we must bail 
            // and cause rejected promise to be returned
            throw err;
        }
        // Previous file exists so reate a new filename
        // xxx/yyy/foo4.txt becomes xxx/yyy/foo5.txt
        var ext = path.extname(filename);
        var filenameWithoutExt = filename.slice(0, -ext.length);
        var number = 0;
        var match = filenameWithoutExt.match(/\d+$/);
        if (match) {
            number = parseInt(match[0], 10);
            filenameWithoutExt = filenameWithoutExt.slice(0, -match[0].length);
        }
        ++number;
        // call this function again, returning the promise 
        // which will cause it to chain onto previous promise
        return nextAvailableFilename(filenameWithoutExt + number + ext);
    });
}

答案 1 :(得分:0)

我想出了一个不依赖于bluebird.promisify的解决方案。 应该处理文件创建失败的情况,原因是它已经存在。

function createFile(path) {
    return new Promise(function (resolve, reject) {
        FileSystem.open(path, 'wx', function (err, fd) {
            if (err) return reject(err);
            FileSystem.close(fd, function (err) {
                if (err) return reject(err);
                return resolve();
            });
        });
    });
}

// todo: make more efficient by multiplying numbers by 2 or something like http://stackoverflow.com/a/1078898/65387

function nextFile(path) {
    return createFile(path).then(function () {
        return path;
    }, function (err) {
        if (err.code !== 'EEXIST') throw err; // error other than "file exists"

        var ext = Path.extname(path);
        var pathWithoutExt = path.slice(0, -ext.length);

        var match = /\d+$/.exec(pathWithoutExt);
        var number = 2;

        if (match) {
            number = parseInt(match[0]) + 1;
            pathWithoutExt = pathWithoutExt.slice(0, -match[0].length);
        }

        return nextFile(pathWithoutExt + number + ext);
    });
}