Posts tagged ‘asynchronous’

Promise Objects in JavaScript

2013-10-07 21:48

JavaScript’s default mode of operation is to rely heavily on callbacks: functions invoked when a longer operation (such as network I/O) finishes and delivers result. This makes it asynchronous, which is a notably different programming style than using blocking operations contained within threads (or their equivalents, like goroutines in Go).

Callbacks have numerous problems, though, out of which the most severe one is probably the phenomenon of “marching to the right”:

  1. doSomething(withTheseArgs, function(result) {
  2.     theDoSomethingElse(function() {
  3.         nowDoThis(toThat, function(result) {
  4.             andThen(function() {
  5.                 stop(hammertime, function(result) {
  6.                     // ...and so on...
  7.                 });
  8.             });
  9.         });
  10.     });
  11. });

When using the (still) common style of providing a callback as the last argument to a function initiating an asynchronous operation, you get this annoying result of ever-increasing indentation as you chain those operations together. It feels like the language itself is telling you that it was not designed for such a complex stuff… Coincidence? ;)

But it gets worse. Operations may fail somewhere along the way, which is something you’d probably like to know about. Depending on the conventions your project, framework or environment uses, this could mean additional boilerplate inside the callbacks to distinguish success from error cases. This is typical in Node.js, where first argument of callback represents the error, if any:

  1. fs.readFile('/etc/passwd', 'utf8', function (err,data) {
  2.   if (err) {
  3.     return console.log("Sorry, no hacking for you (" + err.message + ")");
  4.   }
  5.   console.log(data);
  6. });

Alternatively, you may be asked to provide the error handler separately; an “errback”, as it’s sometimes called. Splitting the code into small parts is great and everything, but here it means you’ll have two functions as arguments:

  1. doSomething(withThis, function(result) {
  2.     // ...
  3. }, function(error) {
  4.     // ...
  5. });

Giving them names and extracting somewhere outside may help readability a little, but will also prevent you from taking advantage of one of the JavaScript’s biggest benefits: superior support for anonymous functions and closures.

Tags: , , , ,
Author: Xion, posted under Programming » 3 comments

The Javascript Functions Which Are Not

2012-06-03 15:25

It would be quite far-fetched to call JavaScript a functional language, for it lacks many more sophisticated features from the FP paradigm – like tail recursion or automatic currying. This puts it on par with many similar languages which incorporate just enough of FP to make it useful but not as much as to blur their fundamental, imperative nature (and confuse programmers in the process). C++, Python or Ruby are a few examples, and on the surface JavaScript seems to place itself in the same region as well.

Except that it doesn’t. The numerous different purposes that JavaScript code uses functions makes it very distinct, even though the functions themselves are of very typical sort, found in almost all imperative languages. Learning to recognize those different roles and the real meaning of function keyword is essential to becoming an effective JS coder.

So, let’s look into them one by one and see what the function might really mean.

A scope

If you’ve seen few good JavaScript libraries, you have surely stumbled upon the following idiom:

  1. /* WonderfulThing.js
  2.  * A real-time, HTML5-enabled, MVC-compatible boilerplate
  3.  * for appifying robust prototypes... or something
  4.  */
  5.  
  6. (function() {
  7.     // actual code goes here
  8. })();

Any and all code is enclosed within an anonymous function. It’s not even stored in a variable; it’s just called immediately so its content is just executed, now.

This round-trip may easily be thought as if doing absolutely nothing but there is an important reason for keeping it that way. The point is that JavaScript has just one global object (window in case of web browsers) which is a fragile namespace, easily polluted by defining things directly at the script level.

We can prevent that by using “bracketing” technique presented above, and putting everything inside this big, anonymous function. It works because JavaScript has function scope and it’s the only type of non-global scope available to the programmer.

A module

So in the example above, the function is used to confine script’s code and all the symbols it defines. But sometimes we obviously want to let some things through, while restricting access to some others – a concept known as encapsulation and exposing an interface.

Perhaps unsurprisingly, in JavaScript this is also done with the help of a function:

  1. var counter = (function() {
  2.     var value = 0;
  3.     return {
  4.         increment: function(by) {
  5.             value += by || 1;
  6.         },
  7.         getValue: function() {
  8.             return value;
  9.         },
  10.     };
  11. })();

What we get here is normal JS object but it should be thought of more like a module. It offers some public interface in the form of increment and getValue functions. But underneath, it also has some internal data stored within a closure: the value variable. If you know few things about C or C++, you can easily see parallels with header files (.h, .hpp, …) which store declarations that are only implemented in the code files (.c, .cpp).

Or, alternatively, you may draw analogies to C# or Java with their public and private (or protected) members of a class. Incidentally, this leads us to another point…

Object factories (constructors)

Let’s assume that the counter object from the example above is practical enough to be useful in more than one place (a tall order, I know). The DRY principle of course prohibits blatant duplication of code such as this, so we’d like to make the piece more reusable.

Here’s how we typically tackle this problem – still using only vanilla functions:

  1. var createCounter = function(initial) {
  2.     var value = initial || 0;
  3.     return {
  4.         increment: function(by) {
  5.             value += by || 1;
  6.         },
  7.         getValue: function() {
  8.             return value;
  9.         },
  10.     };
  11. };
  12. var counter = createCounter();
  13. var counterFrom1000 = createCounter(1000);

Pretty straightforward, right? Instead of calling the function on a spot, we keep it around and use to create multiple objects. Hence the function becomes a constructor for them, while the whole mechanism is nothing else but a foundation for object-oriented programming.

\displaystyle functions + closures = OOP

We have now covered most (if not all) roles that functions play when it comes to structuring JavaScript code. What remains is to recognize how they interplay with each other to control the execution path of a program. Given the highly asynchronous nature of JavaScript (on both client and server side), it’s totally expected that we will see a lot of functions in any typical JS code.

Tags: , , , , ,
Author: Xion, posted under Programming » Comments Off on The Javascript Functions Which Are Not

Kod otwarty na zdarzenia

2010-08-13 18:35

Wydaje mi się, że przynajmniej pod kilkoma względami programowanie przypomina prowadzenie samochodu. Obu tych umiejętności względnie łatwo się nauczyć i niemal niemożliwe jest zapomnieć. Po nabyciu pewnej wprawy mamy też wystarczającą biegłość, by nie musieć koncentrować całej uwagi na którejś z tych czynności. Wyjątkiem są jedynie te miejsca, w których zalecane jest zachowanie szczególnej ostrożności.
Dla mnie (i pewnie nie tylko dla mnie) takimi miejscami w kodzie są styki programu z otoczeniem, przez które musi przebiegać jakaś komunikacja polegająca na wymianie danych i/lub informacji o zdarzeniach. Fragmenty te są ważne i nierzadko problematyczne, gdyż często pociągają za sobą konieczność dopasowania sposobu wykonywania programu do zewnętrznych wymagań. Jak na skrzyżowaniu, trzeba czasem chwilę poczekać i przynajmniej parę razy obejrzeć się dookoła.

W tym kontekście używa się słowa ‘synchronizacja’, jednak kojarzy mi się ono nieodparcie z programowaniem współbieżnym. To trochę złe skojarzenie, bowiem nie trzeba wcale tworzyć kilku wątków czy procesów, by kwestia zaczynała mieć znaczenie. Wystarczą operacje wejścia-wyjścia (zwłaszcza względem mediów wolniejszych niż lokalny system plików) lub obsługa jakiegoś rodzaju zdarzeń zewnętrznych, albo chociażby RPC (Remote Procedure Call – zdalne wywoływanie procedur) – ale oczywiście ta lista nie wyczerpuje wszystkich możliwości. Ogólnie chodzi o wszelkie odstępstwa od możliwości wykonywania programu krok po kroku – a właściwie kroczek za kroczkiem, gdzie każda sekwencja stałej liczby instrukcji zajmuje umowną, niezauważalną chwilę.

Jeśli by się nad tym zastanowić przez moment, to takich sytuacji jest sporo. Ba, właściwie to wspomniane scenariusze sekwencyjne są raczej wyjątkiem, a nie regułą. Dzisiejsze aplikacje działają w wielozadaniowych środowiskach, na ograniczonych pulach zasobów, wymieniają dane przez różne (niekoniecznie szybkie) kanały informacji, i jeszcze dodatkowo są pod ciągłą presją wymagań co do cechy określanej angielskim słówkiem responsiveness – czyli “komunikatywności z użytkownikiem”. Nic dziwnego, że wspomniane przeze mnie wcześniej ‘punkty szczególnej ostrożności’ stają się na tyle istotne, że zazwyczaj to wokół nich buduje się całą architekturę programu.
W jaki sposób są one realizowane? Zależy to od wielu czynników, w tym od rodzaju aplikacji oraz możliwości i struktury systemowego API, które ona wykorzystuje. Tym niemniej można wyróżnić kilka schematów, aplikowalnych w wielu sytuacjach – chociaż nie zawsze z równie dobrym skutkiem. Są nimi:

  • Aktywne czekanie (busy waiting, active polling), polegające na ciągłym dopytywaniu się o rezultat operacji czy też informację o tym, czy oczekiwane zdarzenie zaszło. Stosowane w praktyce sprowadza się do pętli podobnej do poniższej:
    1. while (!WaitFinished()) { }

    i jest przestępstwem ściganym z urzędu w każdym rozsądnym zespole projektowym :) Istnieją aczkolwiek okoliczności łagodzące, zezwalające na jego popełnienie pod ściśle określonymi warunkami. Musimy jedynie być pewni, że zużywanie do 100% czasu procesora przez nasz program jest akceptowalne i że między kolejnymi zapytaniami możemy też zrobić coś produktywnego. Tak się składa, że istnieje typ aplikacji, w którym oba te warunki mogą być spełnione: gry. Ich pętla główna to nic innego jak aktywne czekanie na informacje o zdarzeniach z renderowaniem kolejnych klatek w tak zwanym międzyczasie.

  • Na wpół aktywne czekanie (soft busy waiting) to złagodzona wersja powyższego. Nie zasypujemy już systemu żądaniami z prędkością światła, a jedynie co jakiś czas. Pomiędzy nimi program może zająć się swoimi sprawami albo po prostu… spać:
    1. while (!WaitFinished()) { Sleep(50); }

    Takie krótkie drzemki zdecydowanie zmniejszają zużycie procesora przez aplikację, ale nie dają jej dużego pola manewru. Ta metoda jest więc stosowalna główne dla usług działających w tle. A raczej byłaby, gdyby nie istniały znacznie lepsze :)

  • Przerywalne oczekiwanie (interruptible waiting) to stan zawieszenia w oczekiwaniu na zdarzenie (np. koniec długiej operacji), podczas którego możliwa jest jednak reakcja na pewne szczególne okoliczności. Koncepcja takiego oczekiwania jest zapewne dobrze znana programistom linuksowym, jako że tam niemal każda operacja może zostać przedwcześnie przerwana przyjściem sygnału do procesu. Ów sygnał może być złapany i w konsekwencji obsłużony, lecz nie powinno to pociągać za sobą żadnych skomplikowanych operacji.
    Przerywalne oczekiwanie jest akceptowalnym sposobem działania programów funkcjonujących w tle, na przykład jako usługi systemowe. Niespecjalnie za to nadaje się dla aplikacji z UI – chyba że co jakiś krótki czas pozwolimy na obsłużenie wejścia od użytkownika. Wtedy jednak jest to już raczej wariant soft busy waiting.
  • Asynchroniczne wywołanie zwrotne (asynchronous callback) znaczy mniej więcej tyle, co “zrób to i powiedz mi, kiedy skończysz” albo “powiadom mnie, gdy to się zdarzy”. W obu przypadkach musimy dostarczyć mechanizm wywołania zwrotnego (callback), zintegrowany z naszą aplikacją. To dosyć wygodne, jeśli i bez tego działa ona na zasadzie sterowania zdarzeniami (event-driven) i rzeczone wywołanie możemy potraktować jako jeszcze jedną sytuację do obsłużenia. (Dlatego komponent BackgroundWorker z Windows Forms jest tak prosty w użyciu). Gorzej jest wtedy, gdy na potrzeby asynchronicznego callbacku musimy rozbić na kilka części (i stanów) program, który bez tego działałby niemal sekwencyjnie.
    Osobną kwestią jest też sposób realizacji callbacka. W dużej mierze zależy on od możliwości języka. W jednych jest to bardzo wygodne (C#, Python, większość języków skryptowych), w innych nieco mniej (Java), a w jeszcze innych ledwo da się to zrobić (C++, a jakże).

Między powyższymi sposobami możliwe są “konwersje”, oczywiście do pewnego stopnia. Wymagać to może uruchomienia dodatkowego wątku, w którym wykonujemy polling operację asynchroniczną lub wręcz blokującą, i którego stan możemy odpytywać lub otrzymać jako sygnał na obiekcie synchronizacyjnym po wejściu w stan przerywalnego czekania.
Nieczęsto jednak taka zabawa ma uzasadnienie. W najlepszym razie otrzymamy rozwiązanie równoważne, a najgorszym stracimy na wydajności operacji dostosowanej pod konkretny typ powiadamiania. Lepiej jest jednak trzymać się tego, co dana platforma i API nam proponuje.

Tags: , , , ,
Author: Xion, posted under Programming » 4 comments

Asynchroniczność kontra wątki

2008-03-10 23:02

Niekiedy trzeba zrobić coś czasochłonnego: operację, która nie zakończy się od razu, lecz zabierze zauważalny odcinek czasu. Dość często dotyczy to odczytu (lub zapisu) danych z miejsca, które nie musi być natychmiast dostępne: gniazdka sieciowego, międzyprocesowego potoku (pipe) czy w niektórych sytuacjach nawet pamięci dyskowej. Wówczas rzadko możemy pozwolić sobie na “powieszenie” programu na parę(naście/dziesiąt) sekund w oczekiwaniu, aż zlecona operacja się zakończy. W międzyczasie trzeba bowiem wykonywać też inne czynności, z aktualizacją interfejsu użytkownika na czele.

Typowy rozwiązaniem jest wtedy umieszczenie czasochłonnej czynności w osobnym wątku. Zdarza się jednak, że nie jest to jedyne wyjście. Niekiedy – na przykład przy korzystaniu z gniazd sieciowych w Windows API lub dowolnych strumieni w .NET – dysponujemy alternatywnym sposobem, którym jest zlecenie operacji asynchronicznej. Polega ono na żądaniu wykonania danego działania “w tle” wraz ze sposobem, w jaki chcemy odebrać informację zwrotną. W tym charakterze chyba najczęściej stosuje się funkcje typu callback, podawane – zależnie od języka – jako wskaźniki (C/C++), delegaci (Delphi, C#) lub obiekty implementujące ustalone interfejsy (Java). Po zakolejkowaniu takiego żądania program wykonuje się dalej bez żadnych przerw. Gdy zaś operacja zakończy się, nasz callback zostanie wywołany i w nim będzie można pobrać rezultaty zleconego zadania.
Brzmi całkiem nieźle, prawda? Właściwie można by powiedzieć, że to świetny sposób na uniknięcie stosowania tych strasznych wątków ;-) W praktyce trzeba jednak pamiętać o tym, że:

  • Brak wątków wcale nie oznacza, że nie wystąpią typowe dla wielowątkowości kłopoty – zwłaszcza problemy z synchronizacją dostępu do zasobów. Dotyczą one bowiem każdej sytuacji, kiedy to kod wykonujący się równoległe próbuje uzyskać dostęp do tych samych danych – niezależnie od tego, czy dotyczy to wątków, osobnych procesów ze współdzieloną pamięcią czy właśnie operacji asynchronicznych. Zresztą wewnątrz mechanizmu obsługi takich operacji równie dobrze siedzieć może zwyczajny wątek, który się nimi zajmuje w sposób przezroczysty.
  • Wywołania asynchroniczne “rozgałęziają” wykonywanie kodu w sposób bardziej subtelny (co oznacza: trudniejszy do śledzenia) niż wątki. W przypadku wątków zawsze dobrze widać miejsce rozpoczęcia i zakończenia pracy oraz całą te robotę, napisaną po kolei i w jednym miejscu. Jeśli zaś korzystamy z wywołań asynchronicznych, to kod obsługi wyniku operacji jest oddzielony od kodu, który tę operację zleca. To sprawia, że śledzenie i debugowanie może być trudniejsze i wymagać większej uwagi.

W sumie więc warto pamiętać o tym, że przy wprowadzaniu równoległości trzeba zawsze liczyć z dodatkowymi – nazwijmy to – “kwestiami do rozważenia” :] Unikanie tworzenia wątków za wszelką cenę nie musi zatem być najlepszym wyjściem, skoro koszt rozwiązania alternatywnego bywa podobny.

Tags: ,
Author: Xion, posted under Programming » 5 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.