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

Funkcje nie zwracają wartości

Object Storage Arubacloud
0 głosów
299 wizyt
pytanie zadane 12 lutego 2021 w JavaScript przez Bartx Bywalec (2,120 p.)

Witam, niestety moje funkcje nie zwracają żadnych wartości, mimo tego, że przez console.log czasem da się je zobaczyć. Próbowałem już czterech różnych sposobów (poniżej), żaden nie potrafi odesłać wartości do użytkownika:

exports.findRelation = (req, res) => {
    let startId, endId;
    getStation(req.body.start)
    .then(result => {
        startId = result;
    });
    getStation(req.body.end)
    .then(result => {
        endId = result;
    });
    console.log(startId, endId);
    return res.json({startId, endId});
}

async function getStation (name) {
    await Stations.findOne({name: name}, (err, result) => {
        if (err) return console.log(err);
        else return result._id;
    });
}

 

exports.findRelation = (req, res) => {
    const startId = await getStation(req.body.start);
    const endId = await getStation(req.body.end);
    console.log(startId, endId);
    return res.json({startId, endId});
}

async function getStation (name) {
    await Stations.findOne({name: name}, (err, result) => {
        if (err) return console.log(err);
        else return result._id;
    });
}

 

exports.findRelation = async (req, res) => {
    let stationsId = new Array;
    await Stations.findOne({name: req.body.start}, (err, result) => {
        try {
            stationsId.startId = result._id;
        }
        catch {
            stationsId.startId = null;
        }
    });
    await Stations.findOne({name: req.body.end}, (err, result) => {
        try {
            stationsId.endId = result._id;
        }
        catch {
            stationsId.endId = null;
        }
    });
    console.log(stationsId);
    if (stationsId) return res.json(stationsId);
}

 

exports.findRelation = async (req, res) => {
    let global = new Array;
    await Stations.findOne({name: req.body.start}, (err, result) => {
        stationsId.startId = result._id;
    });
    await Stations.findOne({name: req.body.end}, (err, result) => {
        stationsId.endId = result._id;
    });
    res.json(stationsId);
}

 

W czym może tkwić problem?

1
komentarz 12 lutego 2021 przez icytower Bywalec (2,110 p.)
domyślam się, ze całość jest w express.js. zapytanie wysyłasz z postmana/insomnii czy przeglądarki. spróbuj w tej ostatniej wersji przes res.json(..) dopisać return.
komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)

Tak próbowałem, niestety nie działało, ale przed chwilą, po kilku godzinach znalazłem rozwiązanie. Odpowiedź wykonywała się zanim baza danych zdążyła odpowiedzieć.

exports.findRelation = (req, res) => {
    let startId, endId;
    let start = getStation(req.body.start)
        .then(result => {
            startId = result._id;
        });
    let end = getStation(req.body.end)
        .then(result => {
            endId = result._id;
        });
    Promise.all([start, end])
        .then(() => {
            res.send({ startId, endId });
        });
}

function getStation(name) {
    return Stations.findOne({ name: name }).exec();
}

Pewnie da się to jeszcze jakoś odchudzić.

Dzięki za chęć pomocy.

komentarz 12 lutego 2021 przez icytower Bywalec (2,110 p.)
zrób wszystko na async await, jest czytelniej i mniej kodu. ja na przykład też zawsze lubię zdestrukturyzować req.body na początku kontrolera, potem jest łatwiej z tego korzystać i się tak nie gubię, ale to moje widzimisię chyba :D

ale jakim cudem baza odpowiadała po wysłaniu response skoro przy zapytaniu do bazy miałeś await?
komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)
Nie wiem, też się zastanawiam, choć dla mnie async await to czarna magia, zupełnie tego konceptu nie rozumiem, .then/.catch jest o wiele bardziej zrozumiałe.
komentarz 12 lutego 2021 przez icytower Bywalec (2,110 p.)
koniecznie przełącz się na async await to "lepsza" praktyka.
komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)

No rzeczywiście wygląda to lepiej:

exports.findRelation = async (req, res) => {
    let { _id: start } = await getStation(req.body.start);
    let { _id: end} = await getStation(req.body.end);
    res.send({ start, end });
}

Zamiast

exports.findRelation = (req, res) => {
    let startId, endId;
    let start = getStation(req.body.start)
        .then(result => {
            startId = result._id;
        });
    let end = getStation(req.body.end)
        .then(result => {
            endId = result._id;
        });
    Promise.all([start, end])
        .then(() => {
            res.send({ startId, endId });
        });
}

 

komentarz 12 lutego 2021 przez icytower Bywalec (2,110 p.)
nie ogarniam za bardzo mongoose ale zobacz czy nie da się pobierać tylko tych właściwości, które potrzebujesz, pewnie się da. jeżeli później nie chcesz edytować zmiennych to użyj const to jest zdecydowanie dobra praktyka. domyślnie zawsze używaj const a jeżeli już nie ma innej opcji to wtedy dopiero let.
komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)
Myślałem nad tym czy nie wybierać domyślnie jednej wartości, ale jednak podobna funkcja będzie mi potrzebna jeszcze wielokrotnie, więc wolę wybrać tak jak to jest zrobione, niż robić funkcję pod każdy przypadek.
komentarz 13 lutego 2021 przez icytower Bywalec (2,110 p.)
z tego co widzę w kodzie i tak destrukturyzujesz to co Ci baza zwraca. ja bym chyba tak nie robił. jak baza Ci zwróci Error bo cokolwiek to się wykrzaczy, w szczególności, że zapytanie do bazy nie jest w bloku try catch. imo każde zapytanie do bazy powinno być w try catch.
komentarz 13 lutego 2021 przez Bartx Bywalec (2,120 p.)
Już tak mam jak piszesz. To co wysłałem to była powiedzmy taka wersja alpha

1 odpowiedź

+1 głos
odpowiedź 12 lutego 2021 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli używasz Mongoose lub MongoDB, to podając callback jako ostatni parametr do metody ona nie zwraca promisa, więc nie musisz tego await-ować. Albo await i wtedy bez callbacka, albo callback bez await.

https://mongoosejs.com/docs/api.html#model_Model.findOne

Podpowiedzi zamieściłem w komentarzach w kodzie. Przy okazji - czy któryś z przypadków nie rzuca w konsoli błędem? Funkcje, w których używasz await muszą być asynchroniczne - słówko async - a nie w każdym przypadku u Ciebie tak jest, więc konsola powinna rzucać błędem.


Pierwszy przypadek:

exports.findRelation = (req, res) => {
    let startId, endId;
    getStation(req.body.start)
    .then(result => { // [1]
        startId = result;
    });
    getStation(req.body.end)
    .then(result => { // [2]
        endId = result;
    });
    console.log(startId, endId); // [3] <- to i return poniżej wykonają się przed punktami 1 i 2, ponieważ tam oczekujesz na promisa
    return res.json({startId, endId});
}
 
async function getStation (name) {
    await Stations.findOne({name: name}, (err, result) => {
        if (err) return console.log(err);
        else return result._id; // <- tego nie zwracasz poza metodę Stations.findOne
    });
   // <- tutaj powinieneś zwrócić wartość z wywołania Stations.findOne(..)
}

Powinno działać w ten sposób:

exports.findRelation = async (req, res) => {
    const startId = await getStation(req.body.start);

    const endId = await getStation(req.body.end);

    console.log(startId, endId);
    return res.json({startId, endId});
}
 
function getStation (name) {
    return Stations.findOne({name: name});;
}

Drugi przypadek wygląda ok, tylko zapomniałeś oznaczyć funkcji, w których używasz await jako async i ponowny błąd z brakiem returna w getStation:

exports.findRelation = async (req, res) => { // <- tutaj async
    const startId = await getStation(req.body.start);
    const endId = await getStation(req.body.end);
    console.log(startId, endId);
    return res.json({startId, endId});
}
 
function getStation (name) {
    return Stations.findOne({name: name});
}

Trzeci przypadek - co tutaj jest logowane w console.log(stationsId); pod koniec funkcji? Na oko powinna być tablica z propertisami startId i endId, ale nie w formie indeksowanych elementów, lecz propertisów obiektowych, bo zamiast pushować te zmienne do tablicy, to przypisałeś je jak do obiektu.


Czwarty przypadek - czemu tworzysz zmienną global a później operujesz na zmiennej stationsId? Poza tym, podobnie jak w trzecim przypadku.


Uwaga ogólna - gdy używasz await, to warto ten kod owinąć w try..catch, żeby móc obsłużyć ewentualny błąd.

komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)
Dzięki za pomoc, już znalazłem rozwiązanie. Async Await był zupełnie niepotrzebny, ale musiałem zamknąć odpowiedź w Promise.all aby wykonywała się dopiero po spełnieniu wszystkich obietnic.

Rozwiązanie jest wyżej, ale wersja z tablicą zamiast osobnych zmiennych nie działa.
komentarz 12 lutego 2021 przez ScriptyChris Mędrzec (190,190 p.)

Rozwiązanie jest wyżej, ale wersja z tablicą zamiast osobnych zmiennych nie działa.

 

Czemu? Jak to obsługujesz? 

komentarz 12 lutego 2021 przez Bartx Bywalec (2,120 p.)
Podobnie jak w działającym przykładzie, inicjalizuję pustą tablicę na początku i w .then dodaję do niej wartości. Na samym końcu okazuje się pusta.
komentarz 12 lutego 2021 przez ScriptyChris Mędrzec (190,190 p.)

A wstaw jakiś console.log w miejscu, w którym przypisujesz coś do tablicy w callbacku - możliwe, że to się dzieje po tym jak wykonsolujesz tablicę i odeślesz odpowiedź do klienta.

Podobne pytania

0 głosów
0 odpowiedzi 127 wizyt
pytanie zadane 15 sierpnia 2020 w JavaScript przez Darth_Sith Początkujący (310 p.)
0 głosów
0 odpowiedzi 178 wizyt
pytanie zadane 25 października 2018 w JavaScript przez kaczor32 Nowicjusz (120 p.)
+1 głos
0 odpowiedzi 82 wizyt
pytanie zadane 20 września 2020 w JavaScript przez poldeeek Mądrala (5,980 p.)

92,555 zapytań

141,402 odpowiedzi

319,541 komentarzy

61,939 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.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...