Нодеи выражают и обещают не делать то, что я ожидаю

Я пытаюсь создать API входа в систему, используя NodeJS, но мой код не выполняет то, что я ожидаю. Я очень новичок в JS, обещания и все, поэтому, пожалуйста, упростите любой ответ, если это возможно.

Из того, что я вижу в выводе моего кода, первая часть обещания не ждет, пока функция findUsers(...) закончен.

У меня есть файл маршрутов, где я хочу запустить несколько функций последовательно:

  1. Найти, если пользователь существует в базе данных
  2. if(1 true) хешировать и вводить введенный пароль
  3. ... так далее

Файл маршрутов теперь содержит:

var loginM = require('../models/login');
var loginC = require('../controllers/login');
var Promise = require('promise');

module.exports = function(app) {

    app.post('/login/', function(req, res, next) {

        var promise = new Promise(function (resolve, reject) {
            var rows = loginM.findUser(req.body, res);

            if (rows.length > 0) {
                console.log("Success");
                resolve(rows);
            } else {
                console.log("Failed");
                reject(reason);
            }
        });

        promise.then(function(data) {
            return new Promise(function (resolve, reject) {
                loginC.doSomething(data);

                if (success) {
                    console.log("Success 2");
                    resolve(data);
                } else {
                    console.log("Failed 2");
                    reject(reason);
                }
            });
        }, function (reason) {
            console.log("error handler second");
        });
    });
}

И findUser Функция содержит пул и запрос и находится в файле моделей:

var connection = require('../dbConnection');
var loginC = require('../controllers/login');

function Login() {
    var me = this;
    var pool = connection.getPool();

    me.findUser = function(params, res) {
        var username = params.username;

        pool.getConnection(function (err, connection) {
            console.log("Connection ");

            if (err) {
                console.log("ERROR 1 ");
                res.send({"code": 100, "status": "Error in connection database"});
                return;
            }

            connection.query('select Id, Name, Password from Users ' +
                'where Users.Name = ?', [username], function (err, rows) {
                connection.release();
                if (!err) {
                    return rows;
                } else {
                    return false;
                }
            });

            //connection.on('error', function (err) {
            //    res.send({"code": 100, "status": "Error in connection database"});
            //    return;
            //});
        });
    }
}

module.exports = new Login();

Вывод, который я получаю:

Server listening on port 3000
Something is happening
error handler second
Connection

Итак, что я хочу знать об этом коде, имеет два аспекта:

  1. Почему первое обещание не ждет findUser вернуться, прежде чем продолжить с if/else, и что мне нужно изменить, чтобы это произошло?
  2. Почему error handler second выдвинутый, но не Failed?

Я чувствую, что что-то совершенно не понимаю в обещаниях. Я благодарен за любой ответ. Благодарю.

1 ответ

Решение

Проблемы с кодом

Хорошо, здесь много проблем, поэтому обо всем по порядку.

        connection.query('...', function (err, rows) {
            connection.release();
            if (!err) {
                return rows;
            } else {
                return false;
            }
        });

Это не будет работать, потому что вы возвращаете данные вызывающей стороне, которая является запросом базы данных, который вызывает ваш обратный вызов с err а также rows и не заботится о возвращаемом значении вашего обратного вызова.

Что вам нужно сделать, это вызвать какую-то другую функцию или метод, когда у вас есть строки или когда у вас нет.

Ты звонишь:

var rows = loginM.findUser(req.body, res);

и вы ожидаете получить строки там, но вы не будете. Что вы получите undefined и вы получите его быстрее, чем запрос к базе данных. Это работает так:

me.findUser = function(params, res) {
    // (1) you save the username in a variable
    var username = params.username;

    // (2) you pass a function to getConnection method
    pool.getConnection(function (err, connection) {
        console.log("Connection ");

        if (err) {
            console.log("ERROR 1 ");
            res.send({"code": 100, "status": "Error in connection database"});
            return;
        }

        connection.query('select Id, Name, Password from Users ' +
            'where Users.Name = ?', [username], function (err, rows) {
            connection.release();
            if (!err) {
                return rows;
            } else {
                return false;
            }
        });

        //connection.on('error', function (err) {
        //    res.send({"code": 100, "status": "Error in connection database"});
        //    return;
        //});
    });

    // (3) you end a function and implicitly return undefined
}

pool.getConnection Метод возвращается сразу после передачи функции, еще до того, как будет установлено соединение с базой данных. Затем, через некоторое время, та функция, которую вы передали этому методу, может быть вызвана, но это будет еще долго после того, как вы уже вернулись. undefined к коду, который хотел значение в:

var rows = loginM.findUser(req.body, res);

Вместо того, чтобы возвращать значения из обратных вызовов, вам нужно вызывать некоторые другие функции или методы из них (например, некоторые обратные вызовы, которые вам нужно вызвать, или метод для разрешения обещания).

Возвращение значения является синхронной концепцией и не будет работать для асинхронного кода.


Как следует использовать обещания

Теперь, если ваша функция вернула обещание:

me.findUser = function(params, res) {
    var username = params.username;

    return new Promise(function (res, rej) {

      pool.getConnection(function (err, connection) {
        console.log("Connection ");

        if (err) {
          rej('db error');
        } else {
          connection.query('...', [username], function (err, rows) {
            connection.release();
            if (!err) {
                res(rows);
            } else {
                rej('other error');
            }
        });
      });
    });
}

тогда вы сможете использовать его в какой-то другой части вашего кода следующим образом:

app.post('/login/', function(req, res, next) {

    var promise = new Promise(function (resolve, reject) {

        // rows is a promise now:
        var rows = loginM.findUser(req.body, res);

        rows.then(function (rowsValue) {
            console.log("Success");
            resolve(rowsValue);
        }).catch(function (err) {
            console.log("Failed");
            reject(err);
        });
    });
    // ...

объяснение

Таким образом, если вы выполняете асинхронную операцию - например, запрос к базе данных - тогда вы не можете сразу получить значение, подобное этому:

var value = query();

потому что серверу нужно будет заблокировать ожидание базы данных, прежде чем он сможет выполнить присваивание - и это то, что происходит на каждом языке с синхронным, блокирующим ввод / вывод (поэтому вам нужны потоки на этих языках, чтобы можно было сделано, пока этот поток заблокирован).

В Node вы можете использовать функцию обратного вызова, которую вы передаете асинхронной функции для вызова, когда у нее есть данные:

query(function (error, data) {
  if (error) {
    // we have error
  } else {
    // we have data
  }
});
otherCode();

Или вы можете получить обещание:

var promise = query();
promise.then(function (data) {
  // we have data
}).catch(function (error) {
  // we have error
});
otherCode();

Но в обоих случаях otherCode() будет запущен сразу после регистрации вашего обратного вызова или обработчиков обещаний, прежде чем в запросе появятся какие-либо данные - то есть никакой блокировки не требуется.

Резюме

Идея заключается в том, что в асинхронной, неблокирующей однопоточной среде, такой как Node.JS, вы никогда не делаете больше, чем одну вещь за раз - но вы можете ждать многих вещей. Но вы не просто ждете чего-то и ничего не делаете, пока ждете, вы планируете другие вещи, ждете больше вещей, и в конце концов вам перезвонят, когда он будет готов.

На самом деле я написал небольшой рассказ на Medium, чтобы проиллюстрировать эту концепцию: неблокирующий ввод / вывод на планете Asynchronia256/16 - небольшой рассказ, основанный на неопределенных фактах.

Другие вопросы по тегам