Callback / czy tak działa ? Jak z niego wyjść

Callback / czy tak działa ? Jak z niego wyjść
G5
  • Rejestracja:prawie 6 lat
  • Ostatnio:ponad 2 lata
  • Postów:31
1

Witam
Przechodząc z oop na FP walczę. Próbuję coś działać, ale sam nie wiem czy dobrze tworzę funkcje.
Chodzi mi o takie rozwiązanie. Wiem że stanów powinno się w FP unikać ale mniejsza z tym, może ktoś pomoże.

mamy funkcję które robią kolejne rzeczy :

  • 1 sprawdza czy stan 'running == true ' = > checkThatIsRunning
  • 2 jesli tak to pobiera z taba data - id i przekazuje do kolejnej = > getValuefromTab
  • 3 odbiera data.id z poprzedniej i konwertuje na czas przesyła dalej => convertForTime
  • 4 skonwertowany czas wrzuca w dom do elementu label = >

czy wywołanie funkcji poniżej może następować w taki sposób ? Event jest na parencie bo wykorzystuję go do toogle i pobrania data.id
No i tabów może być nieskończenie wiele.

I kolejne pytanie, co jeśli isRunning jest true, jak mam przerwać ten callback, mam wyłapywać do każdej funkcji drugi argument ?
Albo sprawdzać w każdej kolejnej funkcji czy argument jest nieprawidłowy aż dojdę do najwyższego elementu ?
Prosiłbym o jakąś sugestie. Chciałbym żeby w przypadku isRuning zwrócił np alert, ale nie wykonywał dalej callbacka tylko z tego wyszedł.
W przypadku kiedy isRunnig jest false robi dalej callback i przekazuje dane do elementu.

Znalazłem coś takiego :
https://medium.com/nmc-techblog/functional-error-handling-in-js-8b7f7e4fa092

Staram się coś deklaratywnie zacząć pisać, ale mózg stawia opór. Wiem że ifów powinienem unikać - podobnie statów

Kopiuj
timerOptions.addEventListener("click", function (e) {
  setCountDownTimerLabel(
    convertForTime(getValuefromTab(checkThatIsRunning(e)))
  );
});
Kopiuj
Kolejne funkcje:

const checkThatIsRunning = function (e) {
  if (state.pomodoro.isRunning) {
    return;
  }

const getValuefromTab = function (e) {
  const data = +e.target.closest(".timer__options-btn").dataset.time;
  if (!data) return;
  state.pomodoro.currentTime = data;
  return data;
};

const convertForTime = function (data) {
  const sec = data;
  let minutes = parseInt(sec / 60, 10);
  let seconds = parseInt(sec % 60, 10);
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;
  const time = `${minutes}:${seconds}`;
  return time;
};

const setCountDownTimerLabel = function (data) {
  countDown.innerHTML = data;
  return data;
};
edytowany 7x, ostatnio: guzdziac55
G5
  • Rejestracja:prawie 6 lat
  • Ostatnio:ponad 2 lata
  • Postów:31
0

Przepraszam za spam, ale jak coś to nie chodzi mi o takie rozwiązanie:

Kopiuj
const checkthat = function (e) {
  if (state.pomodoro.isRunning) {
    alert("cos nie tak");
    return;
  }
  setCountDownTimerLabel(convertForTime(getValuefromTab(e)));
};

Mógłbym sprawdzić w pierwszej funkcji np ten warunek i wywołać resztę, ale co jeśli będę chciał w środku jakieś funkcji coś sprawdzić.
Muszę to robić przez promise ? Czy jest inne rozwiązanie. Dla wielu pewnie trywialne jest to co piszę, ale właśnie takie proste rozkminy nie dają mi spać - żeby apka miała ręcę i nogi.

edytowany 1x, ostatnio: guzdziac55
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
2

O ile dobrze rozumiem chcesz osiągnąć efekt by przepływ sterowania nie był zahardkodowany w pierszej funkcji (ani w kolejnych) - całkiem rozsądnie.
Jak ja bym to zrobił? Użyłbym HOF (higher order function) by sterować przepływem danych pomiędzy funkcjami. Najprostsze funkcje tego typu to pipe/compose, które spłaszczają zapis zagnieżdżonych wywołań funkcji, w stylu:

Kopiuj
// Instead of:
d(c(b(a(arg))))

// you can do this:
pipe(a, b, c, d)(arg)

Takie funkcje mogą być dodatkowo wyposażone w logikę kontrolującą błędy (przerywającą łańcuch w przypadku błędu), rozwiązującą promisy itp.

Tu masz przykład zastosowania funkcji rail z pakietu @arrows, która kontroluje przepływ za pomocą rzucanych błędów:

Kopiuj
const { rail } = require('@arrows/composition');

/* Mocking state for example purpose */

const state = {
  isRunning: false, // change to true to see error message
  currentTime: 0
};

const dataset = {
  time: 123 // change to undefined to see error message
};

const fakeOutput = {
  innerHTML: ''
};

/* Steps */

const checkThatIsRunning = () => {
  if (state.isRunning) {
    return new Error('TIMER_ALREADY_RUNNING');
  }

  return dataset;
};

const getValuefromTab = function(dataset) {
  const data = dataset.time;

  if (!data) {
    return new Error('NO_DATA');
  }

  state.currentTime = data;
  return data;
};

const convertForTime = function(data) {
  const sec = data;
  let minutes = parseInt(sec / 60, 10);
  let seconds = parseInt(sec % 60, 10);
  minutes = minutes < 10 ? '0' + minutes : minutes;
  seconds = seconds < 10 ? '0' + seconds : seconds;
  const time = `${minutes}:${seconds}`;
  return time;
};

const setCountDownTimerLabel = function(data) {
  fakeOutput.innerHTML = data;
  return data;
};

/* Composition */

const updateTimer = rail(
  checkThatIsRunning,
  getValuefromTab,
  convertForTime,
  setCountDownTimerLabel
);

/* Execution */

// For simplicity I will skip composing error handling and check it directly,
// but nothing stops you to build a proper pipeline for this:

const result = updateTimer();

const handleErrors = error => {
  switch (error.message) {
    case 'TIMER_ALREADY_RUNNING':
      console.log('Already running!');
      break;
    case 'NO_DATA':
      console.log('No data!');
      break;
  }
};

if (result instanceof Error) {
  handleErrors(result);
} else {
  console.log(`Result: ${result} | Label: ${fakeOutput.innerHTML}`);
}

Live demo: stackblitz (wklep node index.js we wbudowanej konsoli by odpalić program)

Inspiracja dla powstania biblioteki: https://vimeo.com/113707214

Oczywiście to tylko jeden ze sposobów rozwiązania problemu.

edytowany 9x, ostatnio: Maciej Cąderek
G5
co sądzisz o Ramda.js ? W FP jest często wykorzystywana ?
Maciej Cąderek
Maciej Cąderek
Ja z Ramdy korzystam rzadko, ale to ogólnie popularna bilioteka i całkiem nieźle napisana.
G5
  • Rejestracja:prawie 6 lat
  • Ostatnio:ponad 2 lata
  • Postów:31
0

@Maciej Cąderek: Kosmos :) Zastanawiam się jak miałbym na to wpaść, szukałem informacji po zapytaniach googla, znalazłem info o promisach. Rails / torowiska gdzieś mi się obiły o uszy, z logicznego punktu widzenia właśnie te torowiska z przesównymi torami dają taką opcję wychwytywania błędów.

Mam jeszcze jedno pytanie:
Jak to wygląda np w firmie w pracy - można używać od tak np @arrows/composition z npm. Czy założenia projektu mogłyby tego zastrzec - czy to indywidualne.

I jeszcze jedno pytanie. Mógłbym to zrobić na żywca, ale czy takie rozwiązania się stosuje ? Wiadomo że chodzi o utrzymanie kodu i czytelność. Promisem bym mógł to zrobić, ale czym jeszcze ?

To co wysłałeś jest dla mnie mega spoko, tylko czy stosować to teraz wszędzie gdzie natrafię na taki problem.

Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
0

@guzdziac55:

Zastanawiam się jak miałbym na to wpaść, szukałem informacji po zapytaniach googla, znalazłem info o promisach. Rails / torowiska gdzieś mi się obiły o uszy, z logicznego punktu widzenia właśnie te torowiska z przesównymi torami dają taką opcję wychwytywania błędów.

Jak to wygląda np w firmie w pracy - można używać od tak np @arrows/composition z npm. Czy założenia projektu mogłyby tego zastrzec - czy to indywidualne.

Jak ze wszystkim - to zależy. Rzadko klient ma jakieś specjalne wymagania (no chyba, że trafisz na jakąś specyficzną branżę jak banki itp), częściej to od zespołu zależy czy i jakie biblioteki są używane w projekcie. No i żeby FP miało ręce i nogi to cały zespół powinien rozumieć i chcieć pisać w funkcyjnym stylu. Kolejną rzeczą są frameworki typu React/Vue/Angular (czy np Express w przypadku Node'a), które mają często swoje rozwiązania dla podobnych problemów (choć tu też jest pewna elastyczność).

I jeszcze jedno pytanie. Mógłbym to zrobić na żywca, ale czy takie rozwiązania się stosuje ? Wiadomo że chodzi o utrzymanie kodu i czytelność.

Czy jest to popularne rozwiązanie? Raczej nie, ale nie ze względu, że jest to zła praktyka etc. (imo przeciwnie) - raczej po prostu mało ludzi się przejmuje / nie widzi zalet / nie jest zaznajomiona z paradygmatem.

Promisem bym mógł to zrobić, ale czym jeszcze?

Pakowanie takiego synchronicznego kodu w promisy tylko po to by sterować przepływem to imo zła praktyka. Może czegoś nie rozumiem - jak by to miało wyglądać?
Można by to zrobić też w blokach try-catch ale to też imo się bałagan robi szybko i nie jest zbyt przejrzyste.
Mainstreamowe rozwiązanie (z tych bardziej funkcyjnych) to virtual DOM i unidirectional data flow (np. React+Redux).
Kolejna funkcyjna opcja to streamy (np. ReactiveX, Cycle.js).
Ciekawą opcją są też CSP channels - choć to raczej niszowe (popularne w inych językach jak Clojure i Go) i nie znam dobrych bibliotek do tego.
Często można spotkać kombinacje wielu technik, w zależności od konkretnego zadania.

To co wysłałeś jest dla mnie mega spoko, tylko czy stosować to teraz wszędzie gdzie natrafię na taki problem.

A to od ciebe zależy (lub twojego zespołu) - jeśli dobrze pasuje to do danego problemu to czemu nie. Mogę być to trochę stronniczy bo ta akurat biblioteka jest mojego autorstwa, ale większość funkcji w pakiecie to moje wersje powszechnie stosowanych narzedzi funkcyjnych (compose/pipe/tap/curry) obecne w wielu innych bibliotekach i językach programowania. Takie funkcje możesz nawet sobie samemu napisać (nie są to jakieś bardzo skompikowane rzeczy), choć są one tak generyczne, że akurat użycie gotowej libki w tym wypadku ma sens.

Z punktu widzenia kariery to nabezpieczniejszą opcją jest chyba React i jego ekosystem (jeśli chodzi o frontend), który daje gotowe rozwiązania dla części problemów, ale jest na tyle elastyczny, że ogólna wiedza ne temat FP i (szczególności HOF i kompozycji) jest i tak bardzo przydatna by zapanować nad chaosem w projekcie :)

edytowany 15x, ostatnio: Maciej Cąderek
G5
  • Rejestracja:prawie 6 lat
  • Ostatnio:ponad 2 lata
  • Postów:31
0

@Maciej Cąderek: dzięki za poświęcony czas na odpisanie.
Zainteresowałem się FP, bo wcześniej pisałem coś tam w javie - zobaczyłem w es6 klasy i magicznie nagle zacząłem pisać szybciej. Później zauważyłem że te klasy to tak nie całkiem klasy. Dostałem informacje że warto znać, ale FP jest bardziej popularny.

Mam pytanie do osoby ktora lata spędziła z kodem. Wiadomo człowiek uczy się cały czas. Ale twoim zdaniem, z perspektywy doświadczonego usera. Lepiej znając podstawy js szukać inspiracji i stabilizacji w frameworku np Reakcie. Chodzi mi o kwestię wystartowania w branży, zaciągnięcia się na staż - cokolwiek. Czy rzeczy poruszone jak tutaj typu rails też powinienem znać - pisałeś że frameworki mają gotowe rozwiązania - wiadomo że lepiej wiedziec jak cos dziala pod spodem.

No i żeby FP miało ręce i nogi to cały zespół powinien rozumieć i chcieć pisać w funkcyjnym stylu

Rzadko spotykane w zespołach ? Twoim zdaniem warto trzymać się i rozumieć koncepcję FP ?

Sory za pytania, piszę do szuflady, mam kilka szkieletów programów w samym jsie, zarówno pisanych na pałę jak i na klasach. Często dochodząc już do momenu gdzize funkcjonalnosci jest sporo mam zagwozdkę że to co napisałem jest po prostu obrzydliwe. Tzn wiem że mogłem to zrobić lepiej, ale nie znam czegoś czego powinienem. Dziwnie to brzmi, ale tak jest.

szatkus
  • Rejestracja:około 21 lat
  • Ostatnio:około 3 lata
  • Postów:227
1

Warto. Kod możesz pisać nawet wyłącznie na klasach, a i tak stosować idee FP. Po prostu wtedy wychodzi lepszy kod. Więc to czy zespół to stosuje czy nie, dalej znajomość FP się przydaje.

Ale z drugiej strony też nie ma sensu za bardzo się fiksować, bo możesz się zblokować próbując zbudować jakiś idealny kod. Po prostu pisz i z czasem sam zaczniesz zauważać, jakie konstrukcje działają dobrze, a jakie mogą ugryźć w dupę w przyszłości.

A, i w sumie najlepszy sposób, żeby się nauczyć to popisać w jakimś Haskellu czy innym języku nakierowanym na FP. Bo tam po prostu inaczej kodu nie napiszesz, więc odpada zastanawianie się czy to jest FP czy nie FP.


𐤃𐤐𐤀
edytowany 1x, ostatnio: szatkus
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)