stream()
Açıklama
Bir Stream, elektronik tablo uygulamalarındaki hücrelere benzer şekilde, reaktif bir veri yapısıdır.
Örneğin, bir elektronik tabloda A1 = B1 + C1
ise, B1
veya C1
'in değeri değiştiğinde A1
'in değeri otomatik olarak değişir.
Benzer şekilde, bir akışı diğer akışlara bağlayarak birinin değerini değiştirmek otomatik olarak diğerini güncelleyebilir. Bu özellik, maliyetli hesaplamalarınız olduğunda ve bunları her yeniden çizimde çalıştırmak yerine yalnızca gerektiğinde çalıştırmak istediğinizde kullanışlıdır.
Stream'ler Mithril.js'nin temel dağıtımında yer almaz. Stream modülünü eklemek için şunu kullanın:
var Stream = require('mithril/stream');
Ortamınız bir paketleme araç zincirini desteklemiyorsa, modülü doğrudan indirebilirsiniz:
<script src="https://unpkg.com/mithril/stream/stream.js"></script>
Doğrudan bir <script>
etiketiyle ( require
yerine) yüklendiğinde, stream kütüphanesine window.m.stream
üzerinden erişilebilir. Eğer window.m
zaten tanımlıysa (örneğin, ana Mithril.js betiğini de kullandığınız için), stream kütüphanesi mevcut nesneye eklenecektir. Aksi takdirde, yeni bir window.m
nesnesi oluşturulur. Stream'leri Mithril.js ile birlikte doğrudan <script>
etiketleriyle kullanmak istiyorsanız, mithril/stream
betiğinden önce Mithril.js betiğini sayfanıza eklemelisiniz. Aksi takdirde, mithril
, mithril/stream
tarafından tanımlanan window.m
nesnesinin üzerine yazacaktır. Kütüphaneler CommonJS modülleri olarak kullanıldığında ( require(...)
ile) bu bir sorun teşkil etmez.
İmza
Bir akış oluşturur
stream = Stream(value)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
value | any | Hayır | Bu argüman belirtilmişse, akışın değeri bu argümana atanır. |
döner | Stream | Bir akış döndürür |
Statik üyeler
Stream.combine
Bağlı olduğu akışlardan herhangi biri güncellendiğinde otomatik olarak güncellenen, hesaplanmış bir akış oluşturur. Bkz. akışları birleştirme
stream = Stream.combine(combiner, streams)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
combiner | (Stream..., Array) -> any | Evet | birleştirici argümanına bakın |
streams | Array<Stream> | Evet | Birleştirilecek akışların listesi |
döner | Stream | Bir akış döndürür |
birleştirici
Hesaplanan akışın değerinin nasıl üretildiğini tanımlar. Bkz. akışları birleştirme
any = combiner(streams..., changed)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
streams... | Streams 'in splat'ı | Hayır | stream.combine fonksiyonuna ikinci argüman olarak aktarılan akışlara karşılık gelen, sıfır veya daha fazla sayıda akışın değişken sayıda argüman olarak (splat) geçirilmiş hali. |
changed | Array<Stream> | Evet | Bir güncelleme ile değişen akışların listesi |
döner | any | Hesaplanan bir değer döndürür |
Stream.merge
Akış değerlerinden oluşan bir dizi değere sahip akış oluşturur
stream = Stream.merge(streams)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
streams | Array<Stream> | Evet | Bir akış listesi |
döner | Stream | Değeri bir giriş akış değerleri dizisi olan bir akış döndürür |
Stream.scan
Fonksiyonu, bir akümülatör ve gelen değerle birlikte akıştaki her değerde çağırarak yeni bir akış oluşturur.
Akümülatör fonksiyonunun içinde özel stream.SKIP
değerini döndürerek bağımlı akışların güncellenmesini engelleyebileceğinizi unutmayın.
stream = Stream.scan(fn, accumulator, stream)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
fn | (accumulator, value) -> result | SKIP | Evet | Bir akümülatör ve değer parametresi alan ve aynı türde yeni bir akümülatör değeri döndüren bir fonksiyon |
accumulator | any | Evet | Akümülatör için başlangıç değeri |
stream | Stream | Evet | Değerleri içeren akış |
döner | Stream | Sonucu içeren yeni bir akış döndürür |
Stream.scanMerge
Akışlar ve scan fonksiyonlarından oluşan çiftlerin bir dizisini alır ve belirtilen fonksiyonları kullanarak bu akışları tek bir akışta birleştirir.
stream = Stream.scanMerge(pairs, accumulator)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
pairs | Array<[Stream, (accumulator, value) -> value]> | Evet | Akış ve scan fonksiyonları demetlerinden oluşan bir dizi |
accumulator | any | Evet | Akümülatör için başlangıç değeri |
döner | Stream | Sonucu içeren yeni bir akış döndürür |
Stream.lift
Bağlı olduğu akışlardan herhangi biri güncellendiğinde otomatik olarak güncellenen, hesaplanmış bir akış oluşturur. Bkz. akışları birleştirme. combine
fonksiyonundan farklı olarak, bu fonksiyona aktarılan akışlar bir dizi yerine değişken sayıda argüman olarak verilir. Ayrıca, geri çağırma fonksiyonu akış nesneleri yerine akışların değerlerini alır ve changed
parametresi bulunmaz. Bu özellikler, lift
fonksiyonunu uygulamalar için genellikle combine
fonksiyonundan daha kullanıcı dostu hale getirir.
stream = Stream.lift(lifter, stream1, stream2, ...)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
lifter | (any...) -> any | Evet | lifter argümanına bakın |
streams... | Streams listesi | Evet | Kaldırılacak akışlar |
döner | Stream | Bir akış döndürür |
lifter
Hesaplanan akışın değerinin nasıl üretildiğini tanımlar. Bkz. akışları birleştirme
any = lifter(streams...)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
streams... | Streams 'in splat'ı | Hayır | stream.lift fonksiyonuna aktarılan akışların değerlerine karşılık gelen, sıfır veya daha fazla sayıda değerin değişken sayıda argüman olarak (splat) geçirilmiş hali. |
döner | any | Hesaplanan bir değer döndürür |
Stream.SKIP
Aşağı akışların yürütülmesini atlamak için akış geri çağırmalarına döndürülebilen özel bir değer
Stream["fantasy-land/of"]
Bu yöntem işlevsel olarak stream
ile aynı işlevselliğe sahiptir. Fantasy Land'in Uygulanabilir spesifikasyonuna uymak için vardır. Daha fazla bilgi için Fantasy Land nedir bölümüne bakın.
stream = Stream["fantasy-land/of"](value)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
value | any | Hayır | Bu argüman belirtilmişse, akışın değeri bu argümana atanır. |
döner | Stream | Bir akış döndürür |
Örnek üyeleri
stream.map
Değeri geri çağırma fonksiyonunun sonucuna ayarlanan bağımlı bir akış oluşturur. Bu yöntem, stream["fantasy-land/map"]'in bir takma adıdır.
dependentStream = stream().map(callback)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
callback | any -> any | Evet | Dönüş değeri akışın değeri olan bir geri çağırma |
döner | Stream | Bir akış döndürür |
stream.end
Doğru değerine ayarlandığında, bağlı olduğu akışların aboneliğini iptal eden bir akış. Bkz. sona eren durum.
endStream = stream().end
stream["fantasy-land/of"]
Bu yöntem işlevsel olarak stream
ile aynı işlevselliğe sahiptir. Fantasy Land'in Uygulanabilir spesifikasyonuna uymak için vardır. Daha fazla bilgi için Fantasy Land nedir bölümüne bakın.
stream = stream()["fantasy-land/of"](value)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
value | any | Hayır | Bu argüman belirtilmişse, akışın değeri bu argümana atanır. |
döner | Stream | Bir akış döndürür |
stream["fantasy-land/map"]
Değeri geri çağırma fonksiyonunun sonucuna ayarlanan bağımlı bir akış oluşturur. Bkz. akışları zincirleme
Bu yöntem, Fantasy Land spesifikasyonlarına uyumluluk amacıyla sağlanmıştır. Daha fazla bilgi için Fantasy Land nedir bölümüne bakın.
dependentStream = stream()["fantasy-land/map"](callback)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
callback | any -> any | Evet | Dönüş değeri akışın değeri olan bir geri çağırma |
döner | Stream | Bir akış döndürür |
stream["fantasy-land/ap"]
Bu yöntemin adı apply
anlamına gelir. Bir akış a
'nın değeri olarak bir fonksiyonu varsa, başka bir akış b
bunu b.ap(a)
'ya argüman olarak kullanabilir. ap
'yi çağırmak, fonksiyonu akış b
'nin değeriyle argüman olarak çağırır ve fonksiyon çağrısının sonucu olan başka bir akış döndürür. Bu yöntem, Fantasy Land spesifikasyonlarına uyumluluk amacıyla sağlanmıştır. Daha fazla bilgi için Fantasy Land nedir bölümüne bakın.
stream = stream()["fantasy-land/ap"](apply)
Argüman | Tip | Gerekli | Açıklama |
---|---|---|---|
apply | Stream | Evet | Değeri bir fonksiyon olan bir akış |
döner | Stream | Bir akış döndürür |
Temel kullanım
Stream'ler, temel Mithril.js dağıtımının bir parçası değildir. Bunları bir projeye dahil etmek için modülünü çağırın:
var stream = require('mithril/stream');
Değişken olarak akışlar
stream()
bir akış döndürür. En temel düzeyde, bir akış bir değişken veya bir değer okuma ve atama özelliği gibi çalışır: değiştirilebilen durumu tutabilir.
var username = stream('John');
console.log(username()); // "John" yazdırır
username('John Doe');
console.log(username()); // "John Doe" yazdırır
Temel fark, bir akışın bir fonksiyon olması ve bu nedenle daha yüksek dereceli fonksiyonlara dahil edilebilmesidir.
var users = stream();
// fetch API'sini kullanarak bir sunucudan kullanıcıları isteyin
fetch('/api/users')
.then(function (response) {
return response.json();
})
.then(users);
Yukarıdaki örnekte, istek tamamlandığında users
akışı yanıt verileriyle doldurulur.
Çift yönlü bağlamalar
Stream'ler, olay dinleyicileri (event listeners) gibi geri çağırma fonksiyonları aracılığıyla da değer alabilir.
// bir akış
var user = stream('');
// akışa çift yönlü bir bağlama
m('input', {
oninput: function (e) {
user(e.target.value);
},
value: user(),
});
Yukarıdaki örnekte, kullanıcı giriş alanına yazdıkça, user
akışı giriş alanının değeriyle güncellenir.
Hesaplanan özellikler
Stream'ler, hesaplanan özellikleri uygulamak için kullanışlıdır:
var title = stream('');
var slug = title.map(function (value) {
return value.toLowerCase().replace(/\W/g, '-');
});
title('Hello world');
console.log(slug()); // "hello-world" yazdırır
Yukarıdaki örnekte, slug
akışının değeri, slug
akışına erişildiğinde değil, title
akışı güncellendiğinde hesaplanır.
Elbette, birden çok akışa dayalı özellikleri hesaplamak da mümkündür:
var firstName = stream('John');
var lastName = stream('Doe');
var fullName = stream.merge([firstName, lastName]).map(function (values) {
return values.join(' ');
});
console.log(fullName()); // "John Doe" yazdırır
firstName('Mary');
console.log(fullName()); // "Mary Doe" yazdırır
Mithril.js'deki hesaplanan özellikler atomik olarak güncellenir: birden çok akışa bağlı olan akışlar, hesaplanmış özelliğin bağımlılık ilişkisi ne kadar karmaşık olursa olsun, bir değer güncellemesi sırasında yalnızca bir kez çalıştırılır.
Akışları zincirleme
Stream'ler map
yöntemi kullanılarak zincirlenebilir. Zincirlenmiş bir akış, bağımlı akış olarak da bilinir.
// üst akış
var value = stream(1);
// bağımlı akış
var doubled = value.map(function (value) {
return value * 2;
});
console.log(doubled()); // 2 yazdırır
Bağımlı akışlar reaktiftir: değerleri, üst akışlarının değeri güncellendiğinde güncellenir. Bu durum, bağımlı akışın, bağlı olduğu üst akışın değeri atanmadan önce mi yoksa sonra mı oluşturulduğuna bakılmaksızın geçerlidir.
Özel stream.SKIP
değerini döndürerek bağımlı akışların güncellenmesini engelleyebilirsiniz
var skipped = stream(1).map(function (value) {
return stream.SKIP;
});
skipped.map(function () {
// asla çalışmaz
});
Akışları birleştirme
Stream'ler birden fazla üst akışa bağlı olabilir. Bu tür akışlar stream.merge()
fonksiyonu kullanılarak oluşturulabilir.
var a = stream('hello');
var b = stream('world');
var greeting = stream.merge([a, b]).map(function (values) {
return values.join(' ');
});
console.log(greeting()); // "hello world" yazdırır
Veya stream.lift()
yardımcı fonksiyonunu kullanabilirsiniz
var a = stream('hello');
var b = stream('world');
var greeting = stream.lift(
function (_a, _b) {
return _a + ' ' + _b;
},
a,
b
);
console.log(greeting()); // "hello world" yazdırır
Daha karmaşık senaryolarda, reaktif hesaplamalar sırasında akış nesnelerine doğrudan erişim sağlayan stream.combine()
adında daha düşük seviyeli bir fonksiyon da mevcuttur.
var a = stream(5);
var b = stream(7);
var added = stream.combine(
function (a, b) {
return a() + b();
},
[a, b]
);
console.log(added()); // 12 yazdırır
Bir akış herhangi sayıda akışa bağımlı olabilir ve atomik olarak güncellenmesi garanti edilir. Örneğin, bir A akışının B ve C olmak üzere iki bağımlı akışı varsa ve dördüncü bir D akışı hem B hem de C'ye bağımlıysa, A'nın değeri değişirse D akışı yalnızca bir kez güncellenir. Bu durum, D akışı için tanımlanan geri çağırma fonksiyonunun, B akışının yeni bir değeri almasına rağmen C akışının henüz güncellenmediği gibi tutarsız durumlarda çalıştırılmamasını sağlar. Atomiklik ayrıca aşağı akışları gereksiz yere yeniden hesaplamamanın performans avantajlarını da beraberinde getirir.
Özel stream.SKIP
değerini döndürerek bağımlı akışların güncellenmesini engelleyebilirsiniz
var skipped = stream.combine(
function (stream) {
return stream.SKIP;
},
[stream(1)]
);
skipped.map(function () {
// asla çalışmaz
});
Akış durumları
Belirli bir zamanda, bir akış üç durumdan birinde olabilir: beklemede, etkin ve sona erdi.
Beklemede durum
Beklemede olan akışlar, parametre olmadan stream()
çağrılarak oluşturulabilir.
var pending = stream();
Bir akış birden fazla akışa bağımlıysa ve üst akışlarından herhangi biri beklemede durumdaysa, bağımlı akış da beklemede durumdadır ve değerini güncellemez.
var a = stream(5);
var b = stream(); // beklemede olan akış
var added = stream.combine(
function (a, b) {
return a() + b();
},
[a, b]
);
console.log(added()); // tanımsız yazdırır
Yukarıdaki örnekte, added
beklemede olan bir akıştır, çünkü üst öğesi b
de beklemede.
Bu, stream.map
aracılığıyla oluşturulan bağımlı akışlar için de geçerlidir:
var value = stream();
var doubled = value.map(function (value) {
return value * 2;
});
console.log(doubled()); // `doubled` beklemede olduğundan tanımsız yazdırır
Etkin durum
Bir akışa bir değer atandığında etkinleşir (eğer akış sonlandırılmamışsa).
var stream1 = stream('hello'); // stream1 etkin
var stream2 = stream(); // stream2 beklemede başlar
stream2('world'); // sonra etkin hale gelir
Birden çok üst öğesi olan bağımlı bir akış, tüm üst öğeleri etkinse etkin hale gelir.
var a = stream('hello');
var b = stream();
var greeting = stream.merge([a, b]).map(function (values) {
return values.join(' ');
});
Yukarıdaki örnekte, a
akışı etkindir, ancak b
beklemede. b("world")
ifadesi çalıştırıldığında, b
akışı etkinleşir ve bu da greeting
akışının da etkinleşerek değerinin "hello world"
olarak güncellenmesine yol açar.
Sona eren durum
Bir akış, stream.end(true)
çağrılarak bağımlı akışlarını etkilemeyi durdurabilir. Bu işlem, akış ile ona bağlı olan akışlar arasındaki bağlantıyı keser.
var value = stream();
var doubled = value.map(function (value) {
return value * 2;
});
value.end(true); // sona eren duruma ayarla
value(5);
console.log(doubled());
// `doubled` artık `value`'ya bağımlı olmadığından tanımsız yazdırır
Sonlandırılmış akışlar hala durum tutma özelliğine sahiptir; yani, sonlandırıldıktan sonra bile değerlerini okuyabilir ve değiştirebilirsiniz.
var value = stream(1);
value.end(true); // sona eren duruma ayarla
console.log(value(1)); // 1 yazdırır
value(2);
console.log(value()); // 2 yazdırır
Bir akışın sınırlı bir ömrü olduğu durumlarda bir akışı sona erdirmek yararlı olabilir (örneğin, bir DOM öğesi yalnızca sürüklenirken mousemove
olaylarına tepki vermek, ancak bırakıldıktan sonra değil).
Akışları serileştirme
Stream'ler bir .toJSON()
yöntemi uygular. Bir akış JSON.stringify()
'a argüman olarak iletildiğinde, akışın değeri serileştirilir.
var value = stream(123);
var serialized = JSON.stringify(value);
console.log(serialized); // 123 yazdırır
Akışlar yeniden çizimi tetiklemez
Knockout gibi kütüphanelerin aksine, Mithril.js akışları şablonların yeniden işlenmesini tetiklemez. Yeniden çizim, Mithril.js bileşen görünümlerinde tanımlanan olay işleyicilerine, rota değişikliklerine veya m.request
çağrıları tamamlandıktan sonra yanıt olarak gerçekleşir.
Diğer eşzamansız olaylara (örneğin, setTimeout
/setInterval
, websocket aboneliği, 3. taraf kütüphane olay işleyicisi vb.) yanıt olarak yeniden çizim isteniyorsa, m.redraw()
'ı manuel olarak çağırmalısınız.
Fantasy Land nedir
Fantasy Land, ortak cebirsel yapıların birlikte çalışabilirliğini belirtir. Basitçe ifade etmek gerekirse, Fantasy Land spesifikasyonlarına uygun kütüphaneler, bu yapıları nasıl uyguladıklarından bağımsız olarak çalışan, genel amaçlı fonksiyonel kod yazmak için kullanılabilir.
Örneğin, plusOne
adlı genel bir fonksiyon oluşturmak istediğimizi varsayalım. Basit uygulama şöyle görünür:
function plusOne(a) {
return a + 1;
}
Bu uygulamayla ilgili sorun, yalnızca bir sayıyla kullanılabilmesidir. Ancak, a
değişkenine değer atayan mantık, bir hata durumu da üretebilir (örneğin, Sanctuary veya Ramda-Fantasy kütüphanelerinden bir Maybe veya Either yapısı içinde) veya bir Mithril.js akışı, bir Flyd akışı vb. olabilir. İdeal olarak, a
'nın sahip olabileceği her olası tür için aynı fonksiyonun benzer bir sürümünü yazmak istemeyiz ve tekrar tekrar sarma/açma/hata işleme kodu yazmak istemeyiz.
Fantasy Land'in yardımcı olabileceği yer burasıdır. Bu fonksiyonu bir Fantasy Land cebiri açısından yeniden yazalım:
var fl = require('fantasy-land');
function plusOne(a) {
return a[fl.map](function (value) {
return value + 1;
});
}
Şimdi bu yöntem, R.Maybe
, S.Either
, stream
vb. gibi herhangi bir Fantasy Land uyumlu Functor ile çalışır.
Bu örnek karmaşık görünebilir, ancak karmaşıklıkta bir ödünleşimdir: basit plusOne
uygulaması, basit bir sisteminiz varsa ve yalnızca sayıları artırırsanız mantıklıdır, ancak Fantasy Land uygulaması, birçok sarmalayıcı soyutlaması ve yeniden kullanılan algoritma içeren büyük bir sisteminiz varsa daha güçlü hale gelir.
Fantasy Land'i benimseyip benimsemeyeceğinize karar verirken, ekibinizin işlevsel programlama konusundaki aşinalığını göz önünde bulundurmalı ve ekibinizin kod kalitesini koruma konusunda (yeni özellikler yazma ve son teslim tarihlerini karşılama baskısına karşı) taahhüt edebileceği disiplin düzeyi konusunda gerçekçi olmalısınız. Fonksiyonel programlama yaklaşımı, çok sayıda küçük, net bir şekilde tanımlanmış fonksiyonun bir araya getirilmesine, yönetilmesine ve etkin bir şekilde kullanılmasına dayanır. Bu nedenle, yeterli dokümantasyon alışkanlığı olmayan ve/veya fonksiyonel programlama dillerinde deneyimi bulunmayan ekipler için uygun olmayabilir.