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

Tworzenie uniwersalnej funkcji w JavaScript - usuwanie powtarzającego się kodu

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
0 głosów
471 wizyt
pytanie zadane 8 września 2019 w JavaScript przez Kamil Gadawski Użytkownik (570 p.)
zmienione kategorie 9 września 2019 przez Comandeer

Cześć wszystkim - to tak słowem wstępu.

Zacząłem się uczyć Node.js. Stworzyłem małą aplikację która pobiera dane o państwach z popularnego API restcountries

Jak widać na załączonym kodzie, pobieranie danych się powtarza. Chciałbym to wyciągnąć do osobnej funkcji a funkcję wywoływać w każdej z tych metod w zależności od tego co ma robić.

W jaki sposób taką uniwersalną funkcję stworzyć? Co należy wziąć w zaprojektowaniu takiej funkcji? Gotowego rozwiązania nie chcę, tylko takiego szkicu jako to może wyglądać.

const request = require('request')
const urlAPI = require('./urlAPI')

class RequestAPI {
    constructor(name) {
        this.name = name
    }

    getName(name) {
        request(`${urlAPI}/name/${name}`, {json: true}, function (err, res, body) {
            if (err) {
                return console.log('Wystąpił błąd: ', err)
            }
            if (res.statusCode !== 200) {
                return console.log('Nie można wyświetlić informacji na temat kraju. Sprawdź URL.')
            } else {
                console.log(`Nazwa państwa: ${body[0].name}\nStolica: ${body[0].capital}\nWaluta: ${body[0].currencies[0].symbol}\nLiczba ludności: ${body[0].population}`)
            }
        })
    }

    getAllCountry() {
        request(`${urlAPI}/all`, {json: true}, function (err, res, body) {
            if (err) {
                return console.log('Wystąpił błąd: ', err)
            }
            if (res.statusCode !== 200) {
                return console.log('Nie można wyświetlić informacji na temat kraju. Sprawdź URL.')
            } else {
                body.forEach(element => {
                    console.log(`${element.name}`)
                });
            }
        })
    }
}

module.exports = RequestAPI

 

2 odpowiedzi

0 głosów
odpowiedź 9 września 2019 przez Comandeer Guru (606,240 p.)
wybrane 9 września 2019 przez Kamil Gadawski
 
Najlepsza

Akurat w tym wypadku można zrobić funkcję, która przyjmie 2 parametry: URL żądania oraz callback, któremu przekażemy zawartość odpowiedzi. Dodatkowe, opcjonalne ustawienia można schować w zmiennej options.Całość mogłaby wyglądać jakoś tak:

function getFromAPI( URL, callback, options = {} ) {
	request( `${ urlAPI }/${ URL }`, { json: true }, function( err, res, body ) {
		if ( err ) {
			return console.log( 'Wystąpił błąd: ', err );
		}

		if ( res.statusCode !== 200 ) {
			return console.log( options.httpError || `Wystąpił błąd HTTP ${ res.statusCode }` );
		}

		callback( body );
	} );
}

i przykładowe wywołanie:

getFromAPI( `name/${name}`, function( body ) {
	console.log( `Nazwa państwa: ${ body[ 0 ].name }\nStolica: ${ body[ 0 ].capital }\nWaluta: ${ body[ 0 ].currencies[ 0 ].symbol }\nLiczba ludności: ${ body[ 0 ].population }` );
}, {
	httpError: 'Nie można wyświetlić informacji na temat kraju. Sprawdź URL.'
} );

Można sam callback również przerzucić do opcji, a można też wgl się go pozbyć, na rzecz Promise.

komentarz 9 września 2019 przez BT101 Stary wyjadacz (12,540 p.)
Masz funkcje, która przyjmuje url, opcje i callback i owijasz ją w funkcje, która przyjmuje url, opcje i callback ja tego nie kupuje
komentarz 9 września 2019 przez Kamil Gadawski Użytkownik (570 p.)

@Comandeer, poradziłem sobie. Zrobiłem to w następujący sposób.

Stworzyłem nowy plik do którego wrzucam wszystkie funkcje które są jest wykonywane w zależności od polecenia. zawartość pliku:

module.exports = {
    getAllCountryName(body) {
        body.forEach(element => {
            console.log(`${element.name}`)
        });
    },

    getCapital(body){
        console.log(`Nazwa państwa: ${body[0].name}\nStolica: ${body[0].capital}\nWaluta: ${body[0].currencies[0].symbol}\nLiczba ludności: ${body[0].population}`)
    },

    getName(body) {
        console.log(`Nazwa państwa: ${body[0].name}\nStolica: ${body[0].capital}\nWaluta: ${body[0].currencies[0].symbol}\nLiczba ludności: ${body[0].population}`)
    },
}

Następnie w pliku gdzie jest klasa, tymczasowo umieściłem funkcję która wykonuje całe zapytanie do API. Tą funkcję muszę przenieść do osobnego pliku.

function getAPIRequest(url, requestType, callback) {
    request(`${urlAPI}/${url}/${requestType}`, {json: true}, function (err, res, body) {
         if (err || res.statusCode !== 200) {
             return console.log('Nie można wyświetlić informacji na temat kraju. Sprawdź URL.')
         } else {
             callback(body)
         }
     })
 }

Potem w klasie są odpowiednie metody które są wywoływane w zależności od opcji jaką wybierzemy. Czy chcemy szukać po nazwie Państwa, Stolicy czy wylistować wszystkie kraje:

class RequestAPI {
    getName(name) {
        getAPIRequest('name', name, requestType.getName)
    }

    getCapital(capital) {
        getAPIRequest('capital', capital, requestType.getCapital)
    }

    getAllCountry() {
        getAPIRequest('all', requestType.getAllCountryName)
    }
}

@BT101,  poczytaj o callback, to jest klucz do zrozumienia działania tego mechanizmu. 

URL - jest to parametr bez zmienny, który jest zawsze taki sam

Opcje(u mnie jest to parametr (requestType) - typ operacji jaki użytkownik chce wykonać.

Callback - tutaj przekazywana jest w parametrze funkcja która jest zależna od opcji jaką użytkownik wykonuje.

komentarz 9 września 2019 przez Comandeer Guru (606,240 p.)

@BT101, wszystko się rozbija o poziom abstrakcji. W pierwotnym kodzie mamy dwa miejsca blisko siebie w kodzie robiące taką samą rzecz, różniące się tylko dwiema rzeczami: URL-em i sposobem obsługi odpowiedzi. To jest wręcz idealne miejsce do wprowadzenia wspólnej funkcji, która pozwalałaby przejść naszemu kodowi z pokazywania jak coś robi do pokazywania co robi.

@Kamil Gadawski, raczej bym widział tutaj mimo wszystko getAPIRequest( URL, callback, options ), gdzie jedną z opcji byłby typ żądania. Mając tak przygotowaną funkcję, możemy później dodawać dowolne opcje bez zmieniania sygnatury funkcji + można podać domyślne wartości dla tych opcji. Przy okazji: nie sądzisz, że nazwa jest ciut myląca? Ty nie pobierasz żądania z API, tylko chcesz je wykonać (czyli szybciej doAPIRequest).

 

Ogólnie to naskrobałem kiedyś artykuł o abstrakcjach → https://blog.comandeer.pl/o-abstrakcji-slow-kilka.html

komentarz 9 września 2019 przez Kamil Gadawski Użytkownik (570 p.)

@Comendeer masz oczywiście rację z nazwą. Ciężko mi jest zrozumieć options. Do końca nie wiem jak to ma działać i dlatego tego nie użyłem, choć były by to jeszcze bardziej uniwersalne.

komentarz 9 września 2019 przez Comandeer Guru (606,240 p.)
W podlinkowanym wyżej artykule omawiam ten koncept nieco dokładniej.
0 głosów
odpowiedź 8 września 2019 przez BT101 Stary wyjadacz (12,540 p.)
edycja 8 września 2019 przez BT101

Ale tam się nic nie powtarza, uniwersalna funkcja, która robi requesty to jest ten moduł co requirujesz ten "request". Robisz 2 calle w 2 funkcjach ja tu nie widzę kodu, który by się powtarzał. Troche można zrefaktoryzować kilka kwestii:

  • Nie wiem po co returnujesz console.log, watpie zebys to robil z premedytacja
  • Można by stworzyć jakiś plik z http configiem żeby potem jak się coś zmieni nie musieć zmieniać w każdym requeście tylko w 1 pliku
  • Można użyć funkcji strzałkowych (żaden upgrade ale imo ładniej wygląda)
  • Sprawdzenie err i !200 u Ciebie sprowadza się do jednego więc można to połączyć
  • Można by stworzyć plik z jakąś obsługą tych ewentualnych błędów z requestu
  • No i ja osobiście bym wolał użyć czegoś co używa promisów a nie callback funkcje
const request = require('request')
const urlAPI = require('./urlAPI')
const httpConfig = require('./http/config')
const httpError = require('./http/error')
 
class RequestAPI {
    constructor(name) {
        this.name = name
    }
 
    getName(name) {
        request(`${urlAPI}/name/${name}`, httpConfig, (err, res, body) => {
            if (err || res.statusCode !== 200) httpError.handleError();
            if (res.statusCode === 200) {
                console.log(`Nazwa państwa: ${body[0].name}\nStolica: ${body[0].capital}\nWaluta: ${body[0].currencies[0].symbol}\nLiczba ludności: ${body[0].population}`) 
            }
        })
    }
 
    getAllCountry() {
        request(`${urlAPI}/all`, httpConfig, (err, res, body) => {
            if (err || res.statusCode !== 200) httpError.handleError();
            if (res.statusCode === 200) {
                body.forEach(e => console.log(`${e.name}`));
            }
        })
    }
}
 
module.exports = RequestAPI

 

komentarz 9 września 2019 przez Comandeer Guru (606,240 p.)

Przecież nawet w Twoim zrefactoryzowanym przykładzie widać wyraźnie, że cała obsługa żądania i błędów jest taka sama, różni się tylko parsowanie body.

Podobne pytania

0 głosów
0 odpowiedzi 135 wizyt
0 głosów
1 odpowiedź 407 wizyt

93,195 zapytań

142,211 odpowiedzi

322,056 komentarzy

62,519 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 3316p. - dia-Chann
  2. 3251p. - Łukasz Piwowar
  3. 3243p. - Łukasz Eckert
  4. 3222p. - CC PL
  5. 3167p. - Tomasz Bielak
  6. 3157p. - Łukasz Siedlecki
  7. 3133p. - rucin93
  8. 3110p. - Maurycy W
  9. 3028p. - Adrian Wieprzkowicz
  10. 2992p. - Mikbac
  11. 2490p. - Marcin Putra
  12. 2467p. - Michał Telesz
  13. 2427p. - Michal Drewniak
  14. 2372p. - Anonim 3619784
  15. 1949p. - rafalszastok
Szczegóły i pełne wyniki

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 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...