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');
            });
        });
    }
};

Ответ 1

Главное, что следует помнить о promises, состоит в том, что then возвращает новое обещание (как и catch). Как будет достигнуто это новое обещание, зависит от того, что вы вернетесь из обработчика: если вы вернете обещание, новое обещание от then/catch подчинено тому, которое вы вернули; если вы вернете значение, новое обещание будет разрешено с этим значением.

Итак, вы можете связать их вместе. Подумайте о обработчиках then и catch в качестве трансформирующих фильтров, через которые проходит конечный результат.

Обратите внимание, что если у вас есть отправная точка, которая дает вам обещание (cn.connect()), вам не нужно new Promise: просто используйте then и catch, чтобы преобразовать то, что проходит через цепочку, возвращая (новое) значение разрешения.

Еще одна важная вещь, которую следует помнить, состоит в том, что если обработчик catch возвращает значение, он преобразует отклонение в разрешение. Чтобы продолжить путь отклонения, обработчик catch должен либо выбросить исключение, либо вернуть обещание, которое будет/будет отклонено.

Наконец: require вызовы всегда должны быть в начале модуля.

Итак, не удаляя преобразование отказов в разрешения (подробнее об этом в один момент):

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';
            });
    }
};

Примечание. Я намерен НЕ использовать 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';
                            });
                    });
            });
    }
};

Используя его:

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

Я также, возможно, абстрагирую бит, я предполагаю, что вы закончите работу снова и снова: установление соединения и получение от него объекта запроса:

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

function getRequest() {
    var cn = new sql.ConnectionPool(myDao.dbConnection());
    return cn.connect().then(function() {
        return new sql.Request(cn);
    });
}

module.exports = {

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

    CanIConnectToTheDB: function () {
        return getRequest().then(function(req) {
            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';
                        });
                });
        });
    }
};

Ответ 2

Вместо этого:

                .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
});

Или, еще проще, если вы используете async и await:

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

Обратите внимание, что вы можете использовать await внутри функции, созданной с помощью ключевого слова async. В тех местах, где у вас нет встроенной поддержки async и await, вы можете использовать Babel или немного отличающийся синтаксис, основанный на генераторе, как в co или Bluebird coroutines. Для получения дополнительной информации и поддержки в браузерах и Node см. Этот ответ:

Update

Это не проверено, но это более или менее то, как я его написал:

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"));

Дополнительная информация:

Для получения дополнительной информации см. ответы: