node.js~构造Promise的链式序列解析

时间:2017-04-05 17:38:13

标签: javascript node.js promise

有人能建议一种更好的方法来构建Promises的使用吗? 我对Promises很新,我想知道我是否错过了如何构建这一系列事件的方法。

注意:我打算不在这里使用rej [ect]。 你看到guatanrees只有res [olve]返回。这意味着返回的代码只需要一个路径来处理返回的值。 因此,返回的代码在其流程中更为简单。

如果你不认识它,可能会有所帮助,这是从我创建的模块中获取的。把它想象成道。

module.exports = {

    dbConnection: function () {
        return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
    },


    CanIConnectToTheDB: function () {
        return new Promise(function (res, rej) {
            var sql = require('mssql');
            var myDao = require('./myDao');
            var cn = new sql.ConnectionPool(myDao.dbConnection());

            cn.connect().then(function () {
                var req = new sql.Request(cn);
                var qry = 'select serverproperty(\'productversion\') as \'rs\'';
                req.query(qry)
                    .then(function (rs) {
                        qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
                        req.query(qry)
                            .then(function (rss) {
                                res(' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS');
                            })
                            .catch(function (err) {
                                res(' CONNECTED// MASTER DB SUCCESS// ISSUE QUERYING MY DB //' + err + '//');
                            });
                    })
                    .catch(function (er) {
                        res(' CONNECTED// COULD NOT QUERY MASTER DB //' + er + '//');
                    });
            })
            .catch(function () {
                res(' CAN NOT CONNECT');
            });
        });
    }
};

2 个答案:

答案 0 :(得分:5)

而不是像这样:

                .then(function (rs) {
                    qry = '...';
                    req.query(qry)
                        .then(function (rss) {

你可以使用这样的东西:

                .then(function (rs) {
                    qry = '...';
                    return req.query(qry);
                }).then(function (rss) {

即。您可以在一个then回调中返回一个承诺,并在下一个then回调中获得该承诺的已解析值,这样您的缩进就会保持不变。

更简单的例子 - 而不是:

a().then(va => {
    b(va).then(vb => {
        c(vb).then(vc => {
            // you can use vc here
        });
    });
});

你可以这样做:

a().then(va => {
    return b(va);
}).then(vb => {
    return c(vb);
}).then(vc => {
    // you can use vc here
});

或者,如果您使用asyncawait,则更简单:

va = await a();
vb = await b(va);
vc = await c(vb);
// you can use vc here

请注意,您只能在使用await关键字创建的函数中使用async。在您不具备asyncawait原生支持的地方,您可以使用Babel或稍微不同的语法,基于生成器的方法,如co或Bluebird协同程序。有关浏览器和节点的更多信息和支持,请参阅以下答案:

更新

这未经过测试,但这或多或少都是我写的:

module.exports = {

    dbConnection: function () {
        return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
    },

    CanIConnectToTheDB: function () {
        var sql = require('mssql');
        var myDao = require('./myDao');
        var cn = new sql.ConnectionPool(myDao.dbConnection());
        var req;

        return cn.connect()
        .catch(err => Promise.reject('Error 1: ' + err))
        .then(() => {
            req = new sql.Request(cn);
            var qry = 'select serverproperty(\'productversion\') as \'rs\'';
            return req.query(qry)
                .catch(err => Promise.reject('Error 2: ' + err));
        }).then(rs => {
            var qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
            return req.query(qry)
                .catch(err => Promise.reject('Error 3: ' + err));
        }).then(function (rss) {
            return 'CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
        }).catch(err => {
            // if you want it always resolved:
            return 'CAN NOT CONNECT: ' + err;
        });
    }
};

当然,我会保留该函数返回的最终承诺在错误时被拒绝,并且仅在成功时解决,但由于您在问题中明确包含了这个奇怪的要求,所以我按照您的意愿编写了它。

但是如果它拒绝了对任何错误的承诺那么它会更容易使用,特别是如果你关心的只是函数名称中问题的答案 - 我可以连接到数据库:

CanIConnectToTheDB()
    .then(() => console.log("Yes I can"))
    .catch(() => console.log("No I can't"));

更多信息:

有关详情,请参阅以下答案:

答案 1 :(得分:4)

要记住关于promises的关键是then 返回一个新的承诺(和catch一样)。如何解决新的承诺取决于您从处理程序返回的内容:如果您返回一个承诺,then / catch的新承诺将从属于您返回的承诺;如果返回值,则使用该值解析新的promise。

所以你可以将它们连在一起。将thencatch处理程序视为转换最终结果流过的过滤器。

另请注意,如果您的起点可以为您提供承诺(cn.connect()),则您不需要new Promise:只需使用then和{{1}通过返回(新)分辨率值来转换通过链的内容。

要记住的另一个关键事项是,如果catch处理程序返回一个值,它会将拒绝转换为分辨率。要继续拒绝拒绝路径,catch处理程序必须抛出异常或返回将被拒绝的承诺。

最后:catch调用应始终位于模块的开头。

因此,在不删除拒绝转换为解决方案的情况下(稍后会详细介绍):

require
  

注意:我打算不在这里使用rej [ect]。你看到guatanrees只有res [olve]返回。这意味着返回的代码只需要一个路径来处理返回的值。因此,返回的代码在其流程中更为简单。

出于某种原因,拒绝遵循决议的单独路径。它不会使事情变得更复杂,它会使事情更简单。不要将拒绝转换为决议,除非你做了某种错误恢复,并且可以继续,好像拒绝没有发生。

这里的代码允许拒绝拒绝:

var sql = require('mssql');
var myDao = require('./myDao');

module.exports = {

    dbConnection: function () {
        return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
    },


    CanIConnectToTheDB: function () {
        var cn = new sql.ConnectionPool(myDao.dbConnection());
        return cn.connect()
            .then(function () {
                var req = new sql.Request(cn);
                var qry = 'select serverproperty(\'productversion\') as \'rs\'';
                return req.query(qry)
                    .then(function (rs) {
                        qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
                        return req.query(qry)
                            .then(function (rss) { // Note you're not using rss anywhere
                                return ' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
                            })
                            .catch(function (err) {
                                return ' CONNECTED// MASTER DB SUCCESS// ISSUE QUERYING MY DB //' + err + '//';
                            });
                    })
                    .catch(function (er) {
                        return ' CONNECTED// COULD NOT QUERY MASTER DB //' + er + '//';
                    });
            })
            .catch(function() {
                return ' CAN NOT CONNECT';
            });
    }
};

使用它:

var sql = require('mssql');
var myDao = require('./myDao');

module.exports = {

    dbConnection: function () {
        return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
    },

    CanIConnectToTheDB: function () {
        var cn = new sql.ConnectionPool(myDao.dbConnection());
        return cn.connect()
            .then(function () {
                var req = new sql.Request(cn);
                var qry = 'select serverproperty(\'productversion\') as \'rs\'';
                return req.query(qry)
                    .then(function (rs) {
                        qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
                        return req.query(qry)
                            .then(function (rss) { // Note you're not using rss anywhere
                                return ' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
                            });
                    });
            });
    }
};

我也可能抽出一些我认为你最终会反复做的事情:建立连接并从中获取请求对象:

theModule.CanIConnectToTheDB()
    .then(function() {
        // Yes, let's do something
    })
    .catch(function() {
        // No, report the problem, etc.
    });