【问题标题】:Sharing promises between modules vs having multiple promises在模块之间共享 Promise 与拥有多个 Promise
【发布时间】:2015-03-04 22:35:39
【问题描述】:

我正在使用 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;
};

【问题讨论】:

    标签: javascript node.js promise repository-pattern q


    【解决方案1】:

    感觉我使用promise的方式不正确

    是的,您经常使用deferred antipatternPromises do chain,这意味着您可以简单地从您的then 回调中获得return 值甚至承诺,并且.then() 调用将为这些返回值产生承诺。而当你throw时,结果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');
            });
        });
    };
    

    【讨论】:

    • 如果isEmailAvailable() 使用成功/错误路径而不是沿着成功路径发送真/假,那么我想你会发现this.save() 也变得非常简单。
    • @Roamer-1888:看看我的编辑。你是这个意思吗?
    • Bergi,是的,基本上是这样。我可能会忽略“帐户验证错误”抛出,因为它可能会掩盖 Repository.findOne() 抛出的更好的错误消息。
    • @Roamer-1888:是的,也许这样会更好,但我想在行为上保持与 OPs 功能相同,应该掩盖@引发的错误987654335@.
    • 是的,很酷。有了你的解释,OP 就可以选择走哪条路了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 2016-04-20
    相关资源
    最近更新 更多