Zapisanie wartości zmiennej poza con.query z MySQL w NodeJS

0

Witam
Może i głupie pytanie, ale jak zrobić, żeby wartość ustawiona w zapytaniu MySQL wyszła dalej? Cokolwiek ustawie w takim czymś zwraca undefinied. Używam Express i MySQL.
np.

          var zmienna;

          con.query("SELECT ID, userID, numer FROM users WHERE numer='" + req.params.numer + "'", function (err, result, fields) {

            zmienna = "blablabla";
            console.log(zmienna); // zwraca blablabla

          });

          console.log(zmienna); // zwraca undefinied zamiast blablabla
1

Cała zabawa polega na tym, że masz callbacka. Oznacza to, że to co w funkcji nie wykona się od razu, ale wtedy gdy przyjdzie odpowiedź z bazy danych. Wszystko jest asynchroniczne, więc console.log(zmienna) wykona się szybciej (to poza funkcją).

0

@jackweb
Przed chwilą właśnie też to zauważyłem. Dałem zatem opóźnienie na wykonanie tego niżej.

          var zmienna;
 
          con.query("SELECT ID, userID, numer FROM users WHERE numer='" + req.params.numer + "'", function (err, result, fields) {
 
            zmienna = "blablabla";
            console.log(zmienna); // zwraca blablabla
 
          });
          setTimeout(()=>{
             console.log(zmienna); // zwraca blablabla
          },50);

Działać działa, ale nie wydaje mi się, żeby to było poprawne rozwiązanie, bo trzeba ustalić konkretny czas opóźnienia, a nie wiadomo ile dokładnie będą trwać różne zapytania.

0

W żadnym wypadku żadne opóżnienia — nigdy nie przewidzisz dokładnie — albo będziesz zbyt długo czekał, albo się pospieszysz i będzie undefined.
Nie jesteś w stanie zrobić tego "pod spodem"; wrzuć to co masz w Timeout do callbacka (w query). A najlepiej by było zrobić z tego promise'a.

-- edit

Szkic tego co można zrobić przy pomocy obietnic JavaScriptowych :P

function executeQuery() {
    return new Promise((resolve, reject) => {
        con.query("SELECT ID, userID, numer FROM users WHERE numer='" + req.params.numer + "'", (err, result, fields) => {
            if(err) {
                reject("Some error...");
                return;
            }
            
            resolve(result);
        });
    });
}

executeQuery().then(result => {
    console.log("Got the result!!!");
    console.log(result);
}).catch(error => {
    console.log("Ooopss");
    console.error(error);
});

I uważaj no na SQL Injection, bo aż się prosi, żeby coś tam wstrzyknąć złego :P

0

@jackweb: To działa, jednak nadal nie do końca tak jak chcę. Głównie zależy mi na tym, żebym miał funkcję, która zwróci odpowiedź z bazy danych i podstawi to pod jakąś zmienną.

Tak mniej więcej działa ta moja stronka:
title

app.get('/wpis/:id', function (req, res) {

con.query("SELECT ID, userID, tytul, tresc FROM wpisy WHERE ID='" + req.params.id + "'", function (err, result, fields) {

  con.query("SELECT ID, komentarzID, userID, tresc FROM komentarze WHERE wpisID='" + req.params.id + "'", function (err2, result2, fields2) {
            var wpis = {ID: result[0].ID, tytul: result[0].tytul, tresc: result[0].tresc};
            var komentarz = [];

            for(i=0;i<result2.length;i++)
            {
              komentarz[i] = {komentarzUserID: result2[i].userID, komentarzTresc: result2[i].tresc};
            }

            res.render('wpisPage', {wpis: wpis, komentarz: komentarz});

  });
});


});

Chiiałbym oprócz podawania ID użytkownika przesłać jego nick, avatar i inne dane.
W tabeli "Komentarze" przechowuje tylko ID użytkownika, który dany komentarz zamieścił. Potrzebuje wyciągnąć jego nick z tabeli "users" i tu właśnie pojawia się ten problem.

Nawet jeżeli użyje tego Promis'a to i tak nie wiem jak go użyć, żeby działał pod pętlą for dla pobrania kilku użytkowników.
Jeśli zrobię tak to niestety nadal przy res.render jest undefined

var nick[];
for(i=0;i<result2.length;i++)
{
  getUserNick(i).then(result => {
      console.log("Got the result!!!");
      console.log(result);
      nick[i] = result;
  
  }).catch(error => {
      console.log("Ooopss");
      console.error(error);
  });
}

console.log(nick[0]); //UNDEFINED
res.render('wpisPage', {nick: nick}); //UNDEFINED

A nie mogę władować res.render wewnątrz tego wszystkiego, bo to ma się raz na końcu wykonać.
Robienie con.query w pętli równiez nie przynosi efektów:

var nick[];

for(i=0; i<result2.length; i++)
{
      con.query("SELECT ID, nick FROM users WHERE ID='" + i + "'", (err, result, fields) => {
             nick[i] = result[0].nick;
      });
}

console.log(nick); //UNDEFINED
res.render('wpisPage', {nick: nick}); //UNDEFINED

Próbowałem już dziesiątki sposobów, ale dalej nie znalazłem skutecznego rozwiązania (jedynie to z czekaniem setTimeout(), ale to bez sensu).

1
  1. Wiesz, że te kilka zapytań zastąpisz JOINem? Robienie 1+x zapytań – niewydajne.

  2. Znasz Promise.all? Jeśli masz kilka promisów, a chcesz wyniku po wykonaniu wszystkich z nich, to to jest dla Ciebie :P

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

A tak w ogóle, to możesz zrobić to tak:


const express = require('express');
const mysql = require('mysql');

const app = express();
const port = 3000;

const con = mysql.createConnection({
    host: 'localhost',
    user: 'test',
    password: 'test',
    database: 'test'
});

const isNumeric = (n) => {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

const getPostsWithComments = (post_id) => {
    return new Promise((resolve, reject) => {
        if(!isNumeric(post_id)) {
            reject('wrong post_id!');

            return;
        }

        con.query('select posts.id AS post_id, posts.title AS post_title, posts.description AS post_description, comments.title AS comment_title, comments.description as comment_description, comments.id as comment_id from posts left join comments on posts.id = comments.post_id where post_id = ' + post_id + ';', (err, results, fields) => {

            let post = {
                comments: []
            };

            results.forEach(result => {
                post.id = result.post_id;
                post.title = result.post_title;
                post.description = result.post_description;

                if(result.comment_title && result.comment_description) {
                    let comment = {
                        title: result.comment_title,
                        description: result.comment_description,
                        id: result.comment_id
                    };
    
                    post.comments.push(comment);
                }
            });

            resolve(post);
        });
    });
}

app.get('/:id', (req, res) => {
    res.setHeader("Content-Type", "application/json; charset=utf-8");

    getPostsWithComments(req.params.id).then(results => {
        console.log(results);

        res.end(JSON.stringify(results));
    }).catch(err => {
        console.error(err);
        res.end('Oops!');
    });

});

app.listen(8000);


Przy założeniu, że baza wygląda tak:

MariaDB [test]> explain comments;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| title       | varchar(32)      | YES  |     | NULL    |                |
| description | text             | YES  |     | NULL    |                |
| post_id     | int(10) unsigned | YES  | MUL | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

MariaDB [test]> explain posts;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| title       | varchar(32)      | YES  |     | NULL    |                |
| description | text             | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

1 użytkowników online, w tym zalogowanych: 0, gości: 1