• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed node.js Jak połączyć z bazą danych mysql i czekać na odpowiedź

VPS Starter Arubacloud
0 głosów
371 wizyt
pytanie zadane 12 października 2020 w JavaScript przez Utlamo Obywatel (1,540 p.)
zamknięte 12 października 2020 przez Utlamo

Witam tak jak w pytaniu. Mój problem polega na tym że kod wykonuje się troszkę nie poklei. Ogólnie to pisze małego bota dsc i funkcje wykonuje się za każdym wysłaniem wiadomości.
 

if(onConnect){
  connection.query("SELECT * FROM `guild_prefix` WHERE `Guild_id` = " + msg.guild.id, function(err, result, fields){
    if(result.length){
      prefix = result[0].prefix;
    }else {
      prefix = config.default_prefix;
    }
  });
}
if(typeof prefix === 'undefined'){
  console.log("Błąd połączenia z bazą danych. Ustawiono domyślny prefix: " + config.default_prefix);
  prefix = config.default_prefix;
}
if(msg.content.startWith(prefix){msg.reply("prefix działa")}

Tylko jest problem bo pierwsze wykonuje się if i zmienna prefix ustawia na config.default_prefix,,
potem wykonuje się kod niżej,
a na końcu zwracana jest wartość z funkcji  connection.query i prefix ustawiany jest na ten z bazy danych.
Tak więc przy 2 wiadomości już wszystko działa. Więc jak wykonać to proceduralnie(?) czekać?

Przy większej liczbie serwerów discord na przykład 3 gdzie są różne prefixy bot działa niepoprawnie bo cały czas prefix jest ustawiony na ten z ostatniej wiadomości.

komentarz zamknięcia: ScriptyChris pomógł mi zrozumieć problem. Błąd w kodzie już nie występuje.

1 odpowiedź

+1 głos
odpowiedź 12 października 2020 przez ScriptyChris Mędrzec (190,190 p.)
wybrane 12 października 2020 przez Utlamo
 
Najlepsza

Callback z zapytania do bazy wywoływany jest asynchoniczne, a więc później niż pozostałe ify (linie 10 i 14). Jeśli te ify mają wykonać się po ustawieniu prefixa w callbacku, to albo je tam przenieś, albo obsłuż wywołanie do metody query na zasadzie promisa (jeśli metoda tego nie wspiera to przekształć ją w promisa).

komentarz 12 października 2020 przez Utlamo Obywatel (1,540 p.)
W tym kodzie pierwsza opcja za bardzo nie wypali bo samo connection.query() wykonuje się tylko jeżeli połączono z bazą danych. A przy braku połączenia lub błędu bazy danych ma ustawiać config.default_prefix [tu jest on ładowany z pliku].
A wywołanie metody query na zasadzie promise brzmi dobrze tylko że nie bardzo rozumiem co to jest. Mógłbyś wyjaśnić mi na czym to polega?
1
komentarz 12 października 2020 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli nie wiesz czym jest asynchroniczność w JS (a Twoje pytanie o kolejność wykonania kodu na to wskazuje), to polecam zacząć od tego filmu. Co do promisa, odsyłam do tego tutoriala

komentarz 12 października 2020 przez Utlamo Obywatel (1,540 p.)

Dzięki :D
Nie tylko naprawiłem błąd ale też dowiedziałem się dlaczego mój skrypt wykonuje podwójnie funkcje connection.connect() co skutkowało jedynie console.log() dodatkowym, ale jednak nie miałem pojęcia o kolejkowaniu.
Z początku myślałem że rozwiązaniem jest
 

if(warunek){
//łączenie z bazą
}.then{
//masa kodu
}

Ale to załatwiło sprawe

if(warunek){
//łączenie z bazą
}
setTimeout(() => {
//wykonanie następnych czynności
},0)

Nie spodziewałem się w ogóle że setTimeout(x(),0); ma jakiekolwiek zastosowanie i mam nadzieje że nie zrozumiałem wszystkiego na opak :D
 

 

komentarz 12 października 2020 przez ScriptyChris Mędrzec (190,190 p.)
if(warunek){
//łączenie z bazą
}.then{
//masa kodu
}

Takiej konstrukcji nie ma w JS-ie. :) Metoda then jest dostępna na promisach.

if(warunek){
//łączenie z bazą
}
setTimeout(() => {
//wykonanie następnych czynności
},0)

W jakim celu użyłeś tutaj setTimeout z zerowym opóźnieniem? Kod w jego callbacku wykona się w następnej iteracji event loop-a, tylko do czego Ci to potrzebne?

komentarz 12 października 2020 przez Utlamo Obywatel (1,540 p.)

Ponieważ połączenie z bazą danych jest asynchroniczne i dodawane jest do event loop-a.
Ja po prostu wszystkie następne komendy dodaje asynchronicznie do event loop-a(w funkcji strzałkowej), ale za nim, bo samo kolejkowanie jest później.

Więc kolejkuje najpierw zapytanie do bazy danych, a następnie setTimeout() reszte kodu.
W momencie kiedy nie ma połączenia z bazą danych, block if a w tym połączenie z bazą danych zostaje pominięte, więc kolejkuje pozostałe czynności które wykonają się od razu ponieważ event loop jest pusty.
 

W tej reszcie kodu znajdują się wyrażenia

if(typeof prefix === 'undefined'){
  console.log("Błąd połączenia z bazą danych. Ustawiono domyślny prefix: " + config.default_prefix);
  prefix = config.default_prefix;
}
if(msg.content.startWith(prefix)){
msg.reply("prefix działa");
}

Oba wykonują się po przejściu przez blok połączenia z bazą danych czyli po tym

if(onConnect){
  connection.query("SELECT * FROM `guild_prefix` WHERE `Guild_id` = " + msg.guild.id, function(err, result, fields){
    if(result.length){
      prefix = result[0].prefix;
    }else {
      prefix = config.default_prefix;
    }
  });

Więc jeżeli nie ma połączenia block z zapytaniem zostanie pominięty a zmienna prefix nie będzie miała żadnej wartości.

if(typeof prefix === 'undefined')

i wykona się kod nadający wartość zastępczą/awaryjną z config.default_prefix

Wszystko po to aby

if(msg.content.startWith(prefix))
wykonywało się po połączeniu z bazą danych, bo sprawdzam jaki prefix serwer discord ma ustawiony w mojej bazie danych.

komentarz 12 października 2020 przez ScriptyChris Mędrzec (190,190 p.)

Ponieważ połączenie z bazą danych jest asynchroniczne i dodawane jest do event loop-a.
Ja po prostu wszystkie następne komendy dodaje asynchronicznie do event loop-a(w funkcji strzałkowej), ale za nim, bo samo kolejkowanie jest później.

Więc kolejkuje najpierw zapytanie do bazy danych, a następnie setTimeout() reszte kodu.

A co, gdy przybędzie Ci więcej zapytań do bazy lub innych asynchronicznych akcji, albo akcje asynchroniczne będą ze sobą powiązane - np. kilka zapytań do bazy, gdzie każde następne ma się wykonać po poprzednim? Będziesz wtedy żonglował setTimeout'ami? ;)

Używanie timer-ów (setTimeout / setInterval) do czekania na wynik działania jakiejś niezależnej funkcji asynchronicznej to słabe rozwiązanie, gdy są do dyspozycji callbacki/promisy. setTimeout wykona się raz (o ile nie zrobisz go rekurencyjnym), po określonym w parametrze czasie - problem polega na tym, że akcje asynchroniczne, takie jak np. łączenie z bazą w Node, wysłanie Ajaxa, obsługa eventów na stronie (typu click) itp. mają nieokreślony czas zakończenia się (nie wiadomo kiedy się zakończą). Mogą nawet nigdy się nie zakończyć: user nie musi kliknąć w przycisk na stronie; wysłany Ajax może nie dotrzeć do serwera (bo połączenie sieciowe zostanie zerwane), albo serwer się wysypie i nie zwróci odpowiedzi; zapytanie do bazy też może nie zwrócić odpowiedzi (bo np. wysypie się baza). Dlatego ryzykownym jest oczekiwać, że taka akcja zawsze się wykona - po to dostępne są mechanizmy obsługi błędów (np. podejście error-first w callbackach w Node, error callback w jQuery.ajax, czy obsługa odrzuconego promisa); również ryzykownym jest z góry zakładać czas zakończenia takiej akcji. Ustawiłeś setTimeout na 0ms (co jest przez runtime czasem ograniczane do 4ms), co jest znikomą ilością czasu dla akcji asynchronicznej na wykonanie się. Załóżmy jednak, że ustawisz 10 sekund, a po tym czasie spodziewasz się, że zapytanie do bazy zwróci wynik - takie założenie będzie działać, jeśli baza zdąży odpowiedzieć w ciągu 10 sekund. A co jeśli zapytanie potrwa 15, 30, albo 45 sekund? Callback przekazany do setTimeout tego nie wyłapie, bo on nie ma pojęcia że zostało wysłane jakieś zapytanie i on na to czeka. Możesz stwierdzić "ok, to zrobię własnie rekurencyjny setTimeout, albo skorzystam z setInterval" - to czasami ma sens (np. przy pollingu), ale po co cyklicznie wołać jakąś funkcję (która może mieć efekty uboczne), skoro czynność asynchroniczna, którą wykonujesz już oferuje jej obsługę? Dlatego rozsądnie jest oczekiwać na odpowiedź w callbacku przekazanym do funkcji, która rozpoczęła daną akcję asynchroniczną, bo taki callback zostanie wywołany gdy dana akcja się zakończy - będzie więc w pewnym sensie jej świadomy. Przykładowo, zamiast wspomnianego pollingu często stosuje się WebSockets, albo Server Sent Events, żeby nie musieć pingować serwera, tylko po prostu poczekać na konkretną wiadomość, gdy ta się pojawi.

Podobne pytania

0 głosów
0 odpowiedzi 318 wizyt
0 głosów
1 odpowiedź 344 wizyt
pytanie zadane 18 lutego w JavaScript przez Piotrek2713 Mądrala (5,500 p.)
+1 głos
2 odpowiedzi 2,462 wizyt
pytanie zadane 18 listopada 2018 w JavaScript przez lolson Nowicjusz (130 p.)

93,032 zapytań

141,996 odpowiedzi

321,300 komentarzy

62,379 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...