在模块之间共享承诺与拥有多个承诺

Sharing promises between modules vs having multiple promises

我正在使用 Kris Kowal 的 Q 库通过控制器和存储库实现 Node.js 逻辑。我感觉我在下面的示例中使用 promise 的方式不正确。但是我找不到关于如何通过多层或函数使用承诺的正确模式的任何指导。

我做的对吗?实现此逻辑的正确方法是什么?

// 模块:存储库

exports.findOne = function (query) {
    var deferred = Q.defer();

    db.query(query, function (err, data) {
        if (err) {
            deferred.reject(err);
        } else {
            deferred.resolve(data);
        }
    });

    return deferred.promise;
};

//模块:用户

var isEmailAvailable = function (value) {
    var deferred = Q.defer();

    Repository.findOne({email: value})
        .then(function (user) {
            if (user) {
                if (self.id === user.id) {
                    deferred.resolve(true);
                }
                else {
                    deferred.resolve(false);
                }
            } else {
                deferred.resolve(true);
            }
        })
        .fail(function (err) {
            deferred.reject(err);
        });

    return deferred.promise;
};

this.save = function () {
    var deferred = Q.defer();

    isEmailAvailable(this.email)
        .then(function (result) {
            if (result) {
                Repository.upsert(self)
                    .then(function (user) {
                        deferred.resolve(user); //--- Yey!!!
                    }).fail(function () {
                        deferred.reject('Account save error')
                    });
            } else {
                deferred.reject('The email is already in use');
            }
        }).fail(function () {
            deferred.reject('Account validation error')
        });

    return deferred.promise;
};

I have a feeling that the way I use promises is not correct

是的,您使用 deferred antipattern a lot. Promises do chain,这意味着您可以简单地从 then 回调中获得 return 值甚至承诺,并且 .then() 调用将产生对那些 return 价值观的承诺。而当你throw时,result promise会被拒绝

您在存储库模块中使用延迟是正确的,因为 db.query api 需要 promisified。但是用户模块可以大幅收缩,当你已经有承诺时,你不需要使用任何延迟。

function isEmailAvailable(value) {
    return Repository.findOne({email: value})
    .then(function (user) {
        return !user || self.id === user.id;
    });
}

this.save = function() {
    return isEmailAvailable(this.email)
    .then(function (result) {
        if (result) {
            return Repository.upsert(self)
            .then(null, function(err) {
                throw new Error('Account save error');
            });
        } else {
            throw new Error('The email is already in use');
        }
    }, function(err) {
        throw new Error('Account validation error');
    });
};

或者,按照@Roamer-1888 的建议,使用 exceptions for control flow:

function isEmailAvailable(value) {
    return Repository.findOne({email: value})
    .then(function (user) {
        if (user && self.id !== user.id)
            throw new Error('The email is already in use');
    }, function(err) {
        throw new Error('Account validation error');
    });
}

this.save = function() {
    return isEmailAvailable(this.email)
    .then(function () {
        return Repository.upsert(self)
        .then(null, function(err) {
            throw new Error('Account save error');
        });
    });
};