RxJS Temel Kavramları
Hepimizin bildiği gibi Javascript dünyasında asenkron programlama önemli bir yer tutuyor. Günden güne biz yazılım geliştiriciler için asenkron işlemleri daha kolay ve verimli bir şekilde programlamamız amacıyla araçlar ve kütüphaneler geliştiriliyor. Bugün modern Front-end framework’ü olan Angular’ın da framework’ünü üzerine temellendirdiği bu kütüphanelerden RxJS’i konu alacağım. İlk başta öğrenilmesi ve kavramlarını kavraması kolay olmadığı için günlük hayat örneği üzerinden fazla teknik detaya girmeden açıklayacağım. Öğrenmesi ve ustalaşması kolay bir konu olmasa da konu üzerinde hakimiyetiniz arttıkça işlerinizi çok kolaylaştıracak bir kütüphane. İlk olarak RxJS’i anlamak için kısaca reactive programlamaya değineceğim.
Reactive Programlama Nedir?
Reactive programlama, veri akışları (data streams) ve değişikliklerin sisteme yayılması (propagation of change) ile ilgilenen deklaratif bir programlama paradigmasıdır.
y = 100, z= 200;
x$ = z+y;
y = 200, z= 300;
x$ = ?
Tanımı bir örnekle anlamaya çalışalım. Imperative programlamada bu kod çalıştırıldığında x$ ’ın değeri her zaman 300 olacaktır. y ve z’nin değeri daha sonradan güncellense bile x$ ’ın değerinde herhangi bir etkiye neden olmayacaktır. Yukarıdaki kodda x$ bir değişkendir ve y ve z’nin değeri ne zaman değişirse değişsin x$’ın değeri, kodu baştan çalıştırıcak bir ifade olmaksızın otomatik olarak güncellenecektir ve 500 olacaktır. Bu değişim reactive programlamanın bir neticesidir.
RxJS
RxJS (The Reactive Extensions for JavaScript), Reaktif programlamaya dayalı eşzamansız(asynchronous) ve olay tabanlı (event base) programlar oluşturmak için Microsoft tarafından geliştirilen bir kütüphanedir.
RxJS’i daha iyi anlamak için Observable, Observer, Subscription, Operator, Subject, Push vs Pull protocol gibi kavramları bilmemiz gerekiyor.
Kolayca bu kavramları anlamak için market sipariş hizmetlerini ele alabiliriz. Aylık belirli ihtiyaçlarınız için kayıt olduğunuz, belirli aralıklarla fiziksel olarak mağazaya gitmeyip size market ihtiyaçlarınız için teslimat yapan bir firma olduğunu farz edelim. Haftalık yada aylık olarak paketlenmiş market ürünlerini otomatik olarak alıp ve abone olmak için yapacağanız ilk şey bir firmayla anlaşıp kayıt olmak olacaktır. Bu market ürünlerini aldıktan sonra, ürünlerle yemek yapmak veya buzdolabına koymak gibi genellikle bir aksiyon alırsınız. Ürünleri aldıktan sonra reaksiyon gösterdiğiniz için ( çünkü ürünler gelmeden ürünleri buzdolabına koyamaz veya yemek yapamazsınız) bu süreci reaktif programlama olarak düşünebilirsiniz.
Şimdi bu örneğimizi RxJS kavramlarına uyarlayalım.
Yukarıdaki örnekte olduğu gibi market ürünleri teslimat hizmetini Observable olarak düşünelim. Observable teknik anlamda bir fonksiyondur farkı ise fonksiyonlar tek bir değer geriye döndürebilirken ve verinin ne zaman gelmesi gerektiğine veri tüketicisi(pull system) karar verirken Observable’da ise Observable geriye zaman içerisinde birden fazla değer döndürebilir ve bu verilerin ne zaman veri tüketicisine gideceğine veri üreticisi karar verir (push system). Burada parantezler içerisinde belirttiğim iki tane kavrama kısaca değinmiş oldum. Yazının ilerleyen kısımlarında tekrardan bu kavramlara ayrıntılı olarak değineceğim. Observable cold ve hot olmak üzere iki türdedir. Default olarak colddur ve ilk abonelik (subscription) gerçekleştiğinde hot olur. Bu ilk subscription oluncaya kadar veri yayınlamayacağı anlamına gelir. Örneğimizi düşünecek olursak yeni açılan market ürünleri teslimat firması, hiç abonesi olmadığında ürünleri dağıtmayacaktır ve dağıtım yapmak için ilk abonesini(müşterisini) bekleyecektir. Yani Observable’ın veri yayınlayabilmesi için ona abone olmamız gerekir.
Yine bu örnekte Observable(teslimat şirketinden) dan ürün paketlerini(verileri) siz aldığınız için kendinizi Observer olarak düşünebilirsiniz. Arada bir subscription (abonelik) olmadan Observer hiç bir ürün paketi(veri) alamaz. Bu subscription Observer ile Observable arasında bir sözleşme gibidir. Uzun bir süre evde olmadığımızı yazlığa gittiğimizi düşünelim bu süre içerisinde teslimat yapılır ve biz bu ürünleri tüketmezsek ürünler ziyan olmuş olur ve fazladan para kaynağımızı gereksiz yere tüketmiş oluruz. Aynı durum RxJs’i kullanıcağımız uygulamalarımızda da geçerlidir. Örneğin Angular uygulamamızda Observable veriye subscribe olduğumuzu farzedelim ve daha sonrasında başka componente geçiş yaptığımızda subscriptionımızı unsubscribe metodu veya başka yollarla kapatmaz isek gereksiz yere sistem kaynakarını kullanmış olacağız. Bu durumu önlemek için mutlaka Observable kullanılmadığı durumlarda subscription yok edilmelidir.
Bir diğer kavram olan Operatorler Observable’ı özelleştirmenin bir tür yoludur. Operatör kullanarak ne sıklıkla ürün paketlerini(verileri) alacağınızı belirleyebilir, Subscription’ı bekletebilirsiniz, ürün ekleyebilir ve/veya kaldırabilirsiniz.
Dijital olarak internette bir hizmete abone olduğunuzda aile planı(çoklu observers) seçenekleri vardır ve o hizmetten ailenin diğer üyeleri de faydalanabilir. Yani diğer aile üyeleri de aylık veya haftalık gelen aynı market ürünlerini(verileri) Observable’dan (teslimat şirketi) teslim alacaklardır. Subjecler’i ise bu aile planına benzetebiliriz yani birden fazla Observer’a veri akışı sağlayabilirler. Subjectler özel bir Observable türüdür. Observable zaman içerisinde birden fazla değer dönebilen fonksiyon gibi düşünebileceğimizden bahsetmiştik. Observable’a abone olmayı da function call’a benzetebiliriz. Plain Observable’da ona abone olan her Observer için ayrı ayrı execution yapacaktır fakat Subjectlerde her subscriptionda call olmaz dolayısıyla execution olmaz.
import {Observable} from 'rxjs';
let obs = Observable.create(observer => {
observer.next(Math.random());
})
obs.subscribe(res => {
console.log('subscription a :', res); //subscription a :0.2859800202682865
});
obs.subscribe(res => {
console.log('subscription b :', res); //subscription b :0.694302021731573
});
Örnek vericek olursak yukarıda random sayı üreten bir Observable’ımız olsun. Subscription a ve Subscription b ayrı ayrı Observable’ı execute ettiği için farklı değerler alıyor. Bir de Subject örneğimize bakalım;
import {Subject} from 'rxjs';
let obs = new Subject();
obs.subscribe(res => {
console.log('subscription a :', res); // subscription a : 0.91767565496093
});
obs.subscribe(res => {
console.log('subscription b :', res);// subscription b : 0.91767565496093
});
obs.next(Math.random());
Yukarıdaki örnekte gördüğünüz gibi Subject multicast olduğu için tüm Observerlara ayrı ayrı subscription olmasa rağmen aynı değeri döner. Plain Observable ise unicasttir. Plain Observable ile Subjectler arasındaki fark temel olarak böyledir. Subjeclerin de Behavior Subject, Replay Subject, Async Subject ve Void Subject olmak üzere türleri mevcuttur fakat onlara bir başka makalede değineceğim.
Şimdi aynı örneğimiz üzerinden Push ve Pull protokollerini anlamaya çalışalım. Pull sistemlerinde, bu işlemin ne zaman olacağını tüketici yani veriyi üreticiden alan belirler. Üretici verinin ne zaman tüketiciye teslim edileceğinden habersizdir. Tıpkı sizin fiziksel olarak markete gidip oradan ürünleri kendiniz almanız gibi. Ürünleri ne zaman alıcağınızı siz belirlersiniz. Javascript’de Her fonksiyon veya iteratör bir Pull sistemidir. Buna karşın Push sistemlerinde, Üretici(Teslimat hizmeti yapan şirket) verinin ne zaman tüketiciye (size) teslim edileceğini belirler. Tüketici ne zaman ürünün kendisine teslim edileceğinden habersizdir. Promises ve Observablelar en yaygın push sistemleridir. Fakat Observablelar’ın aksine Promiseslar’ın multiple değerleri mouse hareketleri veya bir dosya akışındaki bayt dizileri gibi idare edebilme yeteneği yoktur.
Observable yukarıdaki tabloda görmüş olduğunuz üzere push sisteminde birden fazla değer döndürebilme (multiple) alanını dolduruyor.
RxJS’in teknik detayları ve operatörlerine giriş yapmadan önce genel olarak bilmemiz gereken kavramlara değinmeye çalıştım. Bir sonraki yazıda görüşmek üzere.