Problem z asynchronicznością w node.js

0

Problem polega na tym że funkcje get_username() i get_email() nie działają mimo tego że prawie identyczne funkcje get_theme i get_profpic działają bez zarzutu. Wiem że problem polega na tym że "chcę zjeść pizze zanim zostaje dostarczona", ale jestem dość początkujący w node i nie mam zielonego pojęcia na temat asynchroniczności więc poszkuję pomocy na ten temat.
Mój kod (ten potrzebny):

const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const session = require("express-session");
const cookieParser = require("cookie-parser");
const urlencodedparser = bodyParser.urlencoded({extended:false})
const mysql = require('mysql')

require('dotenv').config();

[...]

app.get('/account/user', (req, res) =>{
  if(req.session.user_id == undefined){
    res.redirect('/account/login')
  }else{
    var theme = get_theme(req);
    var profpic = get_profpic(req);
    var username = get_username(req);
    var email = get_email(req);
    console.log(`${username} ${email}`)
    res.render('user',{
      theme: theme,
      profpic: profpic,
      username: username,
      email: email
    });
  }
})

[...]

const get_theme = (req) => {
  if(req.session.user_id != undefined){
    con.query(`SELECT theme FROM theme WHERE id = ${req.session.user_id};`, (err, result) => {
      if(err) throw err;
      req.session.theme = result[0].theme.toLowerCase();
    })
    req.session.save();
    return req.session.theme;
  }else{
    if(req.session.theme != undefined){
      return req.session.theme;
    }else{
      req.session.theme = 'dark';
      req.session.save();
      return req.session.theme;
    }
  }
}

const get_profpic = (req) => {
  if(req.session.user_id != undefined){
    con.query(`SELECT location FROM profpic WHERE id = ${req.session.user_id};`, (err, result) => {
      if(err) throw err;
      if(result[0] != undefined){
        req.session.profpic = result[0].location;
      }
    })
    if(req.session.profpic == undefined){
      req.session.profpic = '../profpic/default.png'
    }
    req.session.save();
    return req.session.profpic;
  }
}

const get_username = (req) => {
  if(req.session.user_id != undefined){
    con.query(`SELECT username FROM users WHERE id = ${req.session.user_id};`, (err, result) => {
      if(err) throw err;
      if(result[0] != undefined){
        console.log(result[0].username + 'a')
        req.session.username = result[0].username;
      }
    })
    req.session.save();
    console.log(req.session.username + 'b')
    return req.session.username;
  }
}

const get_email = (req) => {
  if(req.session.user_id != undefined){
    con.query(`SELECT email FROM users WHERE id = ${req.session.user_id};`, (err, result) => {
      if(err) throw err;
      if(result[0] != undefined){
        console.log(result[0].email + 'a')
        req.session.email = result[0].email;
      }
    })
    req.session.save();
    console.log(req.session.email + 'b')
    return req.session.email;
  }
}
0

Funkcje get_theme i get_profpic też nie powinny za bardzo działać, tzn może działają kiedy nie odpytują bazy danych.

Ja bym to zrobił tak: (dla przykładu pokażę funkcję get_username)

const get_username = (req) => {
    return new Promise((resolve, reject) => {
        if(req.session.user_id != undefined){
            con.query(`SELECT username FROM users WHERE id = ${req.session.user_id};`, (err, result) => {
              if(err) {
                  reject(err);
                  return;
              }
              if(result[0] != undefined){
                console.log(result[0].username + 'a');
                req.session.username = result[0].username;
                req.session.save();
              }
              resolve(req.session.username);
            })
          }
    })
}

Wtedy funkcja w app.get powinna wyglądać tak

app.get('/account/user', async (req, res) =>{
    if(req.session.user_id == undefined){
      return res.redirect('/account/login')
    }else{
      var theme = get_theme(req);
      var profpic = get_profpic(req);
      var username = await get_username(req);
      var email = get_email(req);
      console.log(`${username} ${email}`)
      return res.render('user',{
        theme: theme,
        profpic: profpic,
        username: username,
        email: email
      });
    }
  })

Co tu się dzieje? Stworzyłem nową promise, która się rozwiązuje w momencie kiedy zostanie zakończone zapytanie do bazy danych (wtedy odpala się resolve), lub wywala błąd (wtedy reject).

Funkcja w app.get w miejscu gdzie jest await "czeka" na odpowiedź z bazy danych. Kiedy ją otrzymuje przechodzi dalej.

Powinieneś teraz w podobny sposób przerobić wszystkie pozostałe funkcje.

Klika uwag:

  • Dlaczego masz dwie osobne funkcje dla pobrania imienia i emaila? Nie możesz wykonać jednego zapytania? 'SELECT username, email FROM users WHERE id'. Im mniej zapytań do bazy tym lepiej.
  • NIGDY nie składaj zapytań do bazy danych w ten sposób. Każdą zmienną trzeba sprawdzić czy nie zawiera czegoś niebezpiecznego. Tutaj niby pobierasz tylko wartość z sesji, więc nie powinno być problemów, ale lepiej dmuchać na zimne. Dlatego zamiast SELECT username FROM users WHERE id = ${req.session.user_id}; może lepiej 'SELECT username FROM users WHERE id = ?', [req.session.user_id], .... Tutaj jest napisane jak to robić
  • Jak już będziesz miał funkcję przerobione na promise, to zamiast await ... await ... await poczytaj o Promise.all().
  • Całość też można zrobić bez korzystania z promise, ale według mnie stanie się nieczytelne
0

Dziękuję bardzo, ale teraz niestety wywala błąd:

var theme = await get_theme(req);
            ^^^^^

SyntaxError: await is only valid in async function

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