trzewiczek info



07.12.2011 kod:blog
Domknięcia i callbacki
Klasycznym przykładem domknięć - prawdopodobnie jednego z napotężniejszych elementów języka JavaScript - jest licznik.
    function counter () {
        var num = 0;

        return {
            add_one: function () {
                num += 1;
                return num;
            },
            get_num: function () {
                return num;
            }
        };
    }

    var my_counter = counter();
    console.log( my_counter.get_num() );
    console.log( my_counter.add_one() );
Proste i zrozumiałe. Dwie ciekawe rzeczy dotyczące tego rozwiązania są takie:
  1. pomimo tego, że funkcja counter kończy swoje działanie po utworzeniu i zwróceniu nowego obiektu, zmienna num przechowywana jest w pamięci, jako że obie metody nowopowstałego obiektu mają do niej dostęp (to właśnie jest domknięcie)
  2. zmienna num dostępna jest jedynie za pomocą dwóch metod nowopowstałego obiektu, co czyni ją prywatną. W ten sposób zwracany obiekt staje się bardzo czytelnym i stuprocentowo szczelnym interfejsem dla stanu przechowywanego w domknięciu.
Fajny przykład i życiowy, bo faktycznie wykorzystywanie domknięć jako wzorca prywatnego stanu z publicznym interfejsem jest niezwykle powszechne. Żeby jednak zrozumieć, jak potężne są domknięcia, przyjrzyjmy się przykładowi z obszaru programowania asynchronicznego.

Jest to wzorzec asynchronicznego dostępu do danych, który pojawił się w Raw Salad. Rzeczy mają się tak. Jest moduł _gui, który odpowiedzialny jest za obsługę zdarzeń w... GUI, moduł _store, który przechowuje aktualny stan aplikacji oraz moduł _db, który w razie potrzeby dociąga dane z bazy. Komunikacja wygląda zatem tak: na klik wyciągam dane ze store'a i wyświetlam je w określony sposób. Jeśli danych nie ma w storze, zaciągam je z bazy, dodaję do store'a i wtedy wyświetlam. W drugim przypadku nie wiadomo, co stanie się na łączach i ile przyjdzie nam czekać na odpowiedź z bazy. Co więcej moduł _gui nie jest w stanie określić z góry, co w storze jest a czego jeszcze nie ma. Zresztą, czy go to powinno interesować? W konsekwencji zatem _gui komunikuje się ze _storem za pomocą takiego callbacka:
  _gui.js

  $('#more-data-please').click( function () {
      var destination = $(this).parent();
      var render_data = function ( from_store ) {
          var new_data = $('<h1>'+from_store+'</h2>');
          destination.append( new_data );
      });

      _store.get_new_data( render_data );
  });

  _store.js

  function get_new_data( callback ) {
      if( data_in_store() ) {
          callback( data_from_store );
      }
      else {
          _db.get_more_data( function( new_data ) {
              store_new_data( new_data );
              callback( new_data );
          });
      }
  }

  _db.js

  function get_more_data( callback ) {
      $.get( '/more_data/', {}, callback );
  }
Oczywiście pewne rzeczy zostały mocno uproszczone - chodzi o wzorzec, a nie konkretny przypadek. Ważne jest to, że dzięki domknięciu callback wysłany z _gui do modułu _store posiada referencję do obiektu destination, dzięki czemu jak tylko dostanie odpowiednie dane, będzie w stanie je wyświetlić. Ten prosty przykład pokazuje, że dzięki domknięciu udało się uzyskać sensowaną modularyzację kodu. Moduł _gui wie, że chce danych, wie jak je wyświetlić i myśli, że są one w storze. Moduł store nie ma pojęcia kto i po co prosi go o dane, wie natomiast, że wraz z zapytaniem przyszła czarna skrzynka z napisem "Do the magic" oraz że jeśli zabraknie mu danych to moduł _db wie skąd je wziąć. Moduł _db natomiast nie wie, kto i po co prosi go o wyjęcie danych z bazy, wie natomiast, jak zagadać do serwera, żeby te dane dostać oraz ma skrzynkę z napisem "All the magic here".

Innymi słowy: w drodze do swojego gabinetu Staszek prosi Janka o kilka faktur; nie ma czasu czekać, bo zaczyna spotkanie, więc zostawia Franka, któremu Janek przekaże te dokumenty, a on już wie, co z nimi zrobić. Ponieważ dwóch faktur nie było, więc Janek prosi Panią Gabrysię o dodatkowe dokumenty. Ostatecznie, kiedy ma już wszystko daje je Frankowi, który idzie do komputera i robi na podstawie tych faktur wykresy potrzebne Staszkowi w dalszej części spotkania, bo ten zostawił mu na swoim biurku pendrive'a z resztą prezentacji. Życiowa sprawa te domknięcia i callbacki...

N E W S