3 puan yazan GN⁺ 2024-04-01 | 1 yorum | WhatsApp'ta paylaş
  • TC39’un JavaScript Signals önerisi, UI durumu ve türetilmiş durumu verimli biçimde izlemek için reaktif ilkel değerleri standartlaştırmaya yönelik erken aşama bir yönelim ve şu anda Stage 1 düzeyinde bir taslak
  • Öneri, uygulama geliştiricilerinin doğrudan kullanacağı yüzey API’sinden çok, framework’lerin paylaşabileceği Signal grafiğinin çekirdek anlambilimi ile otomatik izleme mekanizmasına odaklanıyor
  • Signal.State, Signal.Computed, Signal.subtle.Watcher temel API’ler; hesaplamalar ise lazy evaluation, önbellekleme, otomatik bağımlılık takibi ve glitch-free çalışmayı hedefliyor
  • Yerleşik Signals’ın amacı; Angular, Ember, MobX, Preact, Qwik, Solid, Svelte, Vue gibi çeşitli framework’ler arasında birlikte çalışabilirliği artırmak ve DevTools için hata ayıklama ile performans analizi desteğinin önünü açmak
  • Öneri grubu, Stage 2 öncesinde birden çok prodüksiyon düzeyinde polyfill, framework entegrasyonu, büyük ölçekli uygulama doğrulaması ve performans benchmark’ı yapmayı planlıyor; standardizasyona ulaşması en az 2~3 yıl veya daha fazla sürebilir

JavaScript Signals önerisinin konumu

  • JavaScript Signals, TC39 sürecinde Stage 1 önerisi olarak tanıtılıyor
  • Mevcut belge, ES2015 öncesinde Promise standardizasyonundan önceki Promises/A+’a benzer şekilde, JavaScript ekosisteminde ortak yönü hizalama girişimi olarak görülüyor
  • Doğrudan denenebilecek bir polyfill sunuluyor
  • Öneri şampiyonları ve asıl yazarlar, çeşitli framework ve kütüphanelerden gelen tasarım girdilerine dayanarak mevcut taslağı oluşturdu

Standardizasyonun hedeflediği sorun

  • Karmaşık UI’ler değerleri saklamak, hesaplamak, geçersiz kılmak, senkronize etmek ve görünüm katmanına itmek zorunda; Signals ise bu tekrar eden işler için durum yönetimi altyapısı sağlamayı amaçlıyor
  • Vanilla JS örneğinde counter, isEven, parity, render doğrudan birbirine bağlanıyor ve şu sorunlar ortaya çıkıyor
    • Durum ile render sistemi sıkı biçimde bağlanıyor
    • counter 2’den 4’e değiştiğinde olduğu gibi parity değişmese bile gereksiz hesaplama ve render tetikleniyor
    • Başka UI parçaları counter, isEven, parity içinden yalnızca bazılarına abone olmak istediğinde, manuel abonelik ve abonelikten çıkma yönetimi karmaşıklaşıyor
    • Birden fazla aşamaya pub/sub eklendiğinde boilerplate ve abonelik bookkeeping artıyor, bellek sızıntısı riski doğuyor
  • Signals tabanlı örnek, Signal.State ve Signal.Computed kullanarak değerleri, hesaplamaları ve yan etkileri tek bir modelde ele alıyor
    • Manuel abonelik gerekmiyor
    • Hesaplanmış Signal, hangi Signal’lara bağlı olduğunu otomatik buluyor
    • Hesaplamalar yalnızca değere açıkça ihtiyaç duyulduğunda çalışıyor
    • Hesaplanmış Signal son değeri önbelleğe alıyor

Önerinin temel API’si

  • Signal<T>, get(): T içeren okunabilir bir değer olarak tanımlanıyor
  • Signal.State<T> yazılabilir bir Signal
    • Kurucu, başlangıç değeri ve seçenekleri alıyor
    • Değer get() ile okunuyor, set(t) ile değiştiriliyor
  • Signal.Computed<T>, başka Signal’lara dayanan bir hesaplanmış Signal
    • Callback’in döndürdüğü değerle hesaplanıyor
    • Bağımlılıkları otomatik izliyor
    • Değer lazy olarak hesaplanıyor ve önbelleğe alınıyor
  • Signal.subtle, framework yazarlarına veya DevTools uygulamalarına daha yakın gelişmiş API’leri içeriyor
    • untrack(cb), Signal’ı izleme olmadan okumayı sağlıyor
    • currentComputed(), o anda izlenmekte olan computed Signal’ı döndürüyor
    • introspectSources, introspectSinks, hasSinks, hasSources grafik gözlemi için API’ler
    • Watcher, Signal değişikliklerini algılayarak framework düzeyinde effect ve scheduling uygulamaları için temel sağlıyor
  • SignalOptions<T>, kullanıcı tanımlı karşılaştırma işlevi equals ile watched / unwatched hook’larını destekliyor

Çalışma biçimi ve yürütme modeli

  • Signal, zaman içinde değişebilen bir veri hücresini temsil ediyor ve state ya da computed olarak ayrılıyor
  • Hesaplanmış Signal, çalışırken okuduğu Signal’ları otomatik kaydediyor ve sonraki okumada önceki bağımlılıkların değişip değişmediğini kontrol ediyor
  • Hesaplama pull-based
    • Bağımlılıklar değişse bile hemen yeniden hesaplama yapılmıyor
    • Biri .get() ile okuduğunda gerekirse yeniden hesaplanıyor
  • State Signal yazımları senkron biçimde yansıtılıyor
    • .set() sonrasında o değere bağlı bir computed Signal okunursa gerekirse hemen yeniden hesaplanıyor
    • Yerleşik batching yok
  • Watcher’ın notify callback’i .set() sırasında senkron olarak çalışabiliyor
    • Ancak notify içinde Signal okuma ya da yazma yapılamıyor
    • Asıl okuma/yazma işlemleri daha sonra planlanmalı
  • Computed Signal callback’i istisna fırlatırsa, bu istisna da değer gibi önbelleğe alınıyor ve Signal yeniden okunduğunda tekrar fırlatılıyor

Standardizasyon motivasyonu

  • Her framework’ün Signal uygulaması kendi otomatik izleme mekanizmasına sahip olduğundan, model, bileşen ve kütüphaneleri framework’ler arasında paylaşmak zorlaşıyor
  • Önerinin amacı reaktif modeli render görünümünden ayırmak
    • Geliştiricinin render teknolojisini değiştirdiğinde UI dışı kodu yeniden yazmak zorunda kalmaması hedefleniyor
    • Birden çok bağlamda paylaşılabilecek reaktif modellerin JavaScript ile kurulabilmesi amaçlanıyor
  • Performans ve bellek açısından, yerleşik uygulamanın JS tabanlı uygulamalara göre küçük sabit katsayılarda daha verimli olabileceği, ancak motorun sihirli biçimde algoritmayı değiştirmeyeceği özellikle belirtiliyor
  • DevTools açısından, yerleşik Signals şu bilgileri daha iyi gösterebilir
    • Computed Signal zincirinin call stack’i
    • Signal’lar arasındaki referans grafiği
    • Bellek kullanımı hata ayıklaması için gereken bağımlılık ilişkileri
  • Standart kütüphaneye dahil edilirse, bundle boyutunun küçülmesi, kararlılık ve kalite artışı, projeler arasında ortak bir sözlük oluşması gibi yan etkiler de bekleniyor

Tasarım hedefleri ve kısıtlar

  • Çekirdek işlevler; yazılabilir Signal, hesaplanmış Signal, dirty duruma tepki verme, framework’ün kendi scheduling mekanizması, untrack ve birden çok kod tabanının birleşimini içeriyor
  • Hesaplanmış Signal’lar glitch-free olmayı hedefliyor
    • Gereksiz hesaplamaları önlemek için grafiğin potansiyel olarak dirty bölümleri topolojik sırayla yürütülüyor
    • Tekrarlı hesaplamalar ortadan kaldırılmaya çalışılıyor
  • Framework’lerin kendi scheduling’ini yapabilmesi için Promise tarzı yerleşik zorunlu scheduling eklenmiyor
  • Senkron tepki callback’lerinin kötüye kullanımını engellemek için Watcher’ın notify içinde Signal okuma ve yazma yasaklanıyor
  • untrack, güvenli olmayan bir kaçış kapısı olarak değerlendiriliyor
    • İzleme olmadan okunan bir Signal hesaplama sonucunu etkiliyorsa, bu Signal değiştiğinde computed Signal güncellenmeyebilir
  • API, genel uygulama geliştiricileri için özellikle ergonomik olmaktan çok framework uygulamalarının temeli olmayı önceliklendiriyor

effect ve Watcher

  • Öneride effect() gibi yerleşik bir işlev yok
  • Effect scheduling; framework render döngüsü, disposal ve sahiplik yönetimiyle iç içe olduğundan, JavaScript standart API’si bunu doğrudan çözmüyor
  • Bunun yerine Signal.subtle.Watcher, effect uygulamaları için düşük seviyeli bir temel sunuyor
    • İzlenen Signal’ların bağımlılıkları değiştiğinde notify çağrılıyor
    • getPending() ile hâlâ dirty olan Signal’lar görülebiliyor
    • Dispose gerektiren effect’ler unwatch ile temizlenmeli
  • Watcher’ın izlediği Signal’lar, iç durumlarına erişilebildiği sürece yaşamaya devam edebileceğinden, effect temizliği sırasında Watcher.prototype.unwatch çağrısı gerekiyor

Mevcut taslakta olmayan işlevler

  • Async, mevcut modele dahil değil
    • Signals her zaman senkron olarak değerlendirilebilen değerler olarak ele alınıyor
    • Loading durumunu istisna ile modellemenin bazı yolları bulunuyor; iyileştirme tartışmaları Issue #30 içinde yer alıyor
  • Transactions da dahil değil
    • Ekran geçişlerinde “from” durumu ile “to” durumunu aynı anda korumak, Signal grafik durumunu fork etme sorununu doğuruyor
    • İlgili tartışma Issue #73 içinde yer alıyor
  • Bazı convenience methods de mevcut taslakta bulunmuyor
  • Eksik kalan bu işlevler, framework’ler arasında yeterli uzlaşı olmaması ve üst katmanlarda etrafından dolaşılabilmesi nedeniyle dışarıda bırakıldı; ancak prototip sonrasında yeniden değerlendirilebilir

Geliştirme planı ve standardizasyon takvimi

  • Bu öneri, Nisan 2024’te TC39 Stage 1 gündemine alınmış durumda; belgede mevcut durumun pratikte Stage 0 gibi de görülebileceği belirtiliyor
  • Stage 2 önerilmeden önce şu çalışmalar planlanıyor
    • Birden fazla prodüksiyon düzeyinde polyfill geliştirilmesi
    • Çeşitli framework testleri ve test262 tarzı testlerin geçilmesi
    • Kapsamlı bir signal/framework benchmark seti ile performans doğrulaması
    • Temsili çeşitli JS framework’lerine ve bazı büyük ölçekli uygulamalara önerilen API’nin entegre edilmesi
    • API’nin genişletilebilirliğinin anlaşılması ve nelerin dahil edileceğine karar verilmesi
  • Öneri grubu, yanlış biçimde tasarlanmış Signals’ın çok erken standartlaşmasını önlemek için temkinli ilerlemek istiyor
  • FAQ, standart Signals’ın tarayıcıların genelinde polyfill olmadan kullanılabilir hale gelmesinin en az 2~3 yıl süreceğini öngörüyor
  • Mevcut polyfill bugün kullanılabilir, ancak inceleme sürecinde API değişebileceği için kararlılığına güvenilmemesi tavsiye ediliyor

FAQ’de özetlenen kullanım modeli

  • Yerleşik Signals, render teknolojisinden bağımsız
    • VDOM kullanan Preact, native DOM kullanan Solid, karma yaklaşım kullanan Vue gibi farklı modellerin hepsi mümkün görülüyor
  • Uygulama geliştiricileri için Signals’ı çoğunlukla framework üzerinden kullanmak daha uygun
    • Framework; Watcher, untrack, ownership, disposal ve DOM render scheduling işlerini yönetiyor
  • SSR, hydration ve resumability ile birlikte kullanılabiliyor
    • Qwik, Signals’ı bu özelliklerle birlikte kullanıyor; öneri tarafı da Qwik’in resumable Signals yapısının State ve Computed birleşimiyle modellenebileceğini düşünüyor
  • Signals ile Proxy birbirini tamamlıyor
    • Proxy, sığ nesne işlemlerini yakalıyor; Signals ise veri hücrelerinin bağımlılık grafiğini düzenliyor
    • Proxy arkasında Signals kullanmak, iç içe reaktif yapıları daha ergonomik hale getirebilir
  • Signals, stream değil; mevcut değeri temsil eden hücreler
    • Bir State Signal’a art arda iki kez yazılıp başka hiçbir işlem yapılmazsa, ilk yazım computed Signal ya da effect tarafından hiç görülmeyebilir
    • Belgede bu durum, glitch-free yürütmenin karşı ucundaki bir özellik olarak değerlendiriliyor; stream için async iterable veya observable gibi başka yapıların daha uygun olduğu belirtiliyor

1 yorum

 
GN⁺ 2024-04-01
Hacker News yorumları
  • Saf JavaScript örneğinin aslında okuması ve kullanması daha kolay olduğunu düşünen bir tek ben miyim merak ediyorum
    “Kurulum gürültülü ve boilerplate’i çok” deniyor ama signals örneği de aynı derecede gürültülü ve boilerplate’i fazla görünüyor; üstelik yeni başlayanların anlaması zor yeni bir kavram da ekliyor
    “counter 2’den 4’e değiştiğinde parity değişmiyor ama gereksiz hesaplama ve render yapılıyor” argümanı bana erken memoization gibi geliyor
    UI’ın başka bir bölümü counter güncellemelerine göre render etmek istiyorsa o strawman örneğinin uygun olmadığı doğru; o durumda signals, olay işleme, merkezi durum deposu (Redux türü) gibi başka yöntemler kullanılabilir
    UI’ın başka bir bölümü yalnızca isEven ya da parity’ye bağlıysa, uygulamanın temel yapısı açısından yaklaşımı değiştirebilirsiniz; ama çoğu durumda böyle değil. “Yalnızca parity’ye bağlı bir render fonksiyonunun counter’a abone olması gerektiğini bilmesi gerekiyor” ifadesi de mutlaka haksız bir yük değil; saf hesaplama fonksiyonlarının girdileri anlamayı kolaylaştırma avantajı var

    • Bunun neden erken memoization olarak görüldüğünü anlamıyorum. Bu sadece basit bir fonksiyona indirgenmiş bir örnek ve insanların gerçekten hiç ihtiyaç duymadığı halde böyle bir kullanım senaryosu uydurduğunu düşünmek zor
      UI geliştirmede giderek daha çok kullanılan bir kavram olan signals standardizasyonunu denemek övgüye değer. Boilerplate’in ne kadar olduğu, olay sistemini doğrudan sizin mi yazmanız gerektiği gibi ayrıntı tartışmalarını bir kenara bırakırsak, birden fazla framework signals kullanıyorsa bunun bir nedeni olabilir ve zaman alsa da standartlaştırmayı denemeye değer
    • Katılıyorum. Yalnız Preact’in signal dokümanlarına bakınca bağlam çok daha iyi oturuyor
      https://preactjs.com/guide/v10/signals
      Preact’te signal, props veya context üzerinden ağaçta aşağı inerken yalnızca signal referansı iletiliyor; bileşenler değere değil signal’a baktığı için signal güncellendiğinde bileşeni yeniden render etmeyebilir. Gerçekten de doğrudan ağaçta .valueya erişen bileşene gidilebilir
      Ayrıca signal, değerin ne zaman erişildiğini ve ne zaman güncellendiğini izler; Preact’te bir bileşenin içinde signal’ın .value değerine erişirseniz, o signal değeri değiştiğinde bileşen otomatik olarak yeniden render edilir
    • JavaScript’te reaktivite yerleşik olmadığı için, reaktivite eklemek kaçınılmaz olarak bir soyutlama maliyeti doğurur. Bu, ihtiyaç olduğunda kullanmak içindir; durum yönetmenin varsayılan yolu olmak zorunda değil
      Deneyimime göre en büyük avantaj, reaktif durumu modülerleştirebilmek. Emredici stilde değişimleri izlemek için ek duruma ihtiyaç duyulur ve modülerlik soyutlamayla elde edilir. Yalnızca gerektiğinde kullanmak yeterli
      Basit ama uygulanabilir bir örnek oluşturmak bir denge meselesi. Reaktivitenin açıkça fayda sağladığı durumlar genelde daha karmaşık olduğundan, bunları göstermek basit ama daha az uygulanabilir örneklere göre daha zor
    • Bunu açıklama biçiminde iyileştirme payı var. Küçük örneklerde sorun pek görünmüyor, daha büyük ölçekte ortaya çıkıyor. PR’lara açığız
    • Belirli bir karmaşıklık eşiği aşıldığında tasarım değişikliği gerektirmesi, kaçınmaya değer bir durum. Saf JS yaklaşımının durum grafiği karmaşıklığı açısından ölçeklenme sınırı var; asıl sorun eşiğin öncesi ve sonrasındaki kullanılabilirlik değil, o eşik aşıldığı anda kullanılabilirliğin kesintili biçimde değişmesi
  • JavaScript’e Promises eklenirken her yerde new Promise yazmak zorunda kalacağım diye itici gelmişti
    Gerçekte ise doğrudan new Promise yazdığım durumları iki elin parmaklarıyla sayabilirim. Bunun yerine özellikle üçüncü taraf kütüphanelerle çalışırken .then’i çok daha sık kullanır oldum
    Sonuçta Promise’ın JavaScript’e eklenmesinin gündelik etkisi, üçüncü taraf kütüphanelerin sunduğu çeşitli özel davranışlar ve özellikler için oldukça basit, genel olarak sağlam ve neredeyse evrensel bir arayüz sağlaması oldu. Dosya okuma olsun, API isteği olsun, derleme adımı çıktısı olsun, .then(res => …) yazınca zaten çalışabilir bir şeyin yarısına gelmişsiniz gibi hissediliyor
    Bu Signal önerisi, reaktif UI framework’lerinin Kambriyen patlaması içinde benzer bir rol oynarsa desteklerim. Dahası, reaktivitenin UI dışına taşmasına da yardımcı olabilir. UI durumu olmayan şeyler için artımlı yeniden hesaplama yapan durum ağaçlarını sık sık hayal etmişimdir

    • Promises’ın esasen async/await eklemek için geldiğini düşünüyordum; yaşam kalitesini gerçekten ciddi artıran da oydu. Pratikte doğrudan new Promise yazmak nadiren gerekiyor
      İlk Promise .then kullanımı, iç içe delegate’lerden büyük bir iyileştirmeydi ve basit zincirler için fena değil; ama koşula bağlı olarak farklı Promise’leri zincirlemek, belirli zincirlerde farklı hata işleme yapmak veya erken dönüş gerektiğinde kod çok daha zor okunur ve yönetilir hale gelebiliyor
      async/await ile çağrıları Promise değilmiş gibi yazabilir, belirli Promise çağrılarının etrafına kolayca try/catch koyabilir ve erken dönüşü de doğal biçimde yapabilirsiniz
  • Bunun neden dilin bir parçası olması gerektiğini anlamıyorum. Kütüphane olarak yapılabiliyor ve zaten böyle kütüphaneler var. Küçük olduğu için koda dahil etmek de büyük bir yük değil; dile eklemek başlı başına bir hedef olmamalı
    Mevcut JS UI kütüphanelerinin signals’ı çok iyi tasarladığı için bunun dilin bir parçası olması gerektiğini düşünmek kibirli bir yaklaşım. signals için farklı ödünleşimlere sahip pek çok uygulama var ve bunların hiçbiri JavaScript spesifikasyonunda özel bir yer edinmeyi hak etmiyor
    Bu kütüphaneler signals kullanmadan önce sanal DOM kullanıyordu. Neyse ki sanal DOM JS’in bir parçası olmadı; signals’ın farkı ne? Farkı yok. Standartlaştırılması gerektiğine dair argüman, sanal DOM dönemindekinden bile daha zayıf
    Web’i bozmadan artık istemediğimiz özellikleri kaldırmanın fiilen bir yolu olmayan bir runtime’a, moda olan her şeyi yığacak mıyız? Oldukça miyopça

    • Haklı noktaları var. Yanlış olanı istemiyoruz ama doğru olanı istiyoruz
      Reaktif UI kazandı. Küçük uygulamalarda bile durum yönetimi yaparken karmaşıklığın patlaması, saf JS kullanmayı zorlaştıran temel şey. Benim için herhangi bir reaktif framework saf JS’ten daha iyi; öyleyse eksik bir yapı taşı olabilir
      Aradan yaklaşık 10 yıl geçtiğine göre, standartlaştırılabilecek sınır noktasını düşünmenin zamanı geldi. Promise gibi doğru yapılırsa çok yaygın kullanım senaryolarının karmaşıklığını azaltabilir
      Daha iyi değerlendirme, “mevcut reaktif framework’ler bu öneriyi kullanır mı?” diye sormaktır. Kullanmazlarsa nedenini, neyin eksik olduğunu, neyin gereksiz olduğunu ve diğer dillerin UI ile reaktivite yaklaşımlarından ne öğrenilebileceğini görmek gerekir. Dağınık deneyimi damıtmanın değeri var
    • Signals’ı standartlaştırmak için iyi nedenlerden biri, hata ayıklamanın kâbus gibi görünmesi. Hesaplanmış signal’lardan oluşan derin bir ağacın birbirini zincirleme tetiklediğini ve bu zincirleme reaksiyonun başlangıç noktasını bulmanız gerektiğini hayal edin. Standartlaştırılırsa geliştirici araçları bunun etrafında oluşturulabilir
    • Standart kütüphanenin büyük kısmı için aynı şey söylenebilir. Ancak motivasyonda belirtildiği gibi, JS’in nispeten küçük standart kütüphanesini genişletip yaygın işler için her seferinde paket getirmeye gerek bırakmama yönünde bir eğilim var
      Bu ihtiyacın kendisi tartışılabilir, ama standart kütüphane genişletilecekse popüler olanlara bakmak iyi bir yaklaşım gibi görünüyor
      signals, sanal DOM’un yerine geçen bir şey değildir
    • Observable önerisini hatırlatıyor
  • Uygulamanın tamamına bir şey sinyallemek gerektiğinde event kullanılır
    window.dispatchEvent(new Event('counterChange'));
    Ve uygulamanın tepki vermek isteyen herhangi bir bölümü şöyle abone olabilir
    window.addEventListener('counterChange', () => { ... do something ... });
    Bu yöntemde sorun ne?

    • Tarihsel olarak bu örneğin kendisi, web’in jQuery’ye evrilip oradan Angular ve React dünyalarına ayrılmasının nedeni
      Event işleme çok kolay dağınık hale gelir. Daha derine bakmak için event bubbling ve propagation konularına bakın
      Büyük uygulamalarda sağlam event işleme gerekir; bu da Angular, Vue gibi framework’lerin bugün pek görünür olmayan avantajlarından biridir
      Framework olmadan standart event işleme API’sini olduğu gibi kullanmak istemezsiniz. Çok sayıda öğe için ekleme, silme, kopyalama, tetikleme, kaldırma, tek seferlik tetikleme gibi işleri yönetirken ciddi istenmeyen yan etkiler oluşabilir
    • Metne göre event yayımlayıcı/observable yapıları, birden çok kez çağrıldıklarında gereksiz işlere yol açar
      signals ile farkı, sonuç değerinin yalnızca nihai tüketici değeri okuduğunda hesaplanmasıdır. Signal’a gerçek yazma anı ile asenkron render güncellemesi planlamasını ayırır; gözlemcinin yürüttüğü hesaplama zinciri render sırasında yalnızca bir kez çalışır
      signal üzerinden gönderilen ara değerler kaybolur; bu yüzden onların içinde çok ilginç işler yapmak zordur ve aslında render döngüsünü koordine etmeye yönelik daha üst düzey bir soyutlama katmanına yakındır
    • Signals da sonuçta publish/subscribe’dır, ama API’si kullanımı daha rahattır. Çünkü listener’lar otomatik olarak eklenip kaldırılır
      Performansı da daha iyi olabilir. Örneğin iki değere bağlı bir hesaplama olduğunu düşünelim: result = a ? b : 0 ifadesinde a false ise b değişse bile yeniden hesaplamak gerekmez. signals’ta bu otomatik olur; geleneksel publish/subscribe ile ise epey kod gerekir
    • Bu deseni 10 yılı aşkın süredir kullanıyorum. Zor olan şu: Zamanla bir listener başka event’leri tetikleyebilir, başka bir event de ilk rutine geri dönerek bitmeyen bir listener döngüsü oluşturabilir
      Tüm listener’ların böyle zincirleme tetiklemeler oluşturmayacağını garanti etmek de zordur
    • Bu yöntemde, önerinin vurguladığı publish/subscribe mimarisinin tüm dezavantajları var
  • İnsanların onlarca yıldır durum takibini ve DOM güncellemelerini neden bu kadar zor bulduğunu anlamaya çalışıyorum
    Elbette biraz disiplin gerekiyor, ama bana birkaç yılda bir çıkan çözümlerden çok daha basit geliyor. Backbone, Knockout, Angular, React, dilin kendisini değiştirme vb.; sanırım benim düşünme biçimim temelden farklı
    Fonksiyon adlarında bile belli oluyor. innerTexti güncellemeye “render” deniyor ama aslında render etmek değil. Olsa olsa bunu tarayıcı render eder; boyama ve ilgili diğer tüm işler için de aynı şey geçerli. En basit DOM fonksiyonlarından birini karmaşık hale getirmeye yönelik çaresiz bir çaba gibi geldiği için gerçekten şaşırtıcı

    • Basit uygulamalarda kolay
      Daha karmaşık hale gelince kolay değil
    • “Biraz disiplin gerekiyor” sözünden, eskiden bir ASM programcısı olarak çılgın taşınabilir C programcılarına, sonra da bir C programcısı olarak çılgın bellek güvenli Java programcılarına kızacak biri olduğuna dair güçlü bir his alıyorum
      Programlamadaki ilerleme, iyi sonuçlar almak için gereken törensel ve katı disiplini ortadan kaldırma süreci olarak görülebilir
      React'in bir sonraki evrim olduğunu söylemiyorum, ama signals kesinlikle doğru yönde atılmış bir adım
    • Onlarca yıl uzun bir süre. Tarayıcıdan tarayıcıya DOM güncellemenin ne kadar karmaşık olduğunu hatırlarsınız
      DOM'u veri durumuyla senkronize etmek başlı başına çok zor değil, ama bunu 60fps'te çok performanslı yapmak inanılmaz derecede zor. Özellikle de sızdırmayan ama aşırı zahmetli de olmayan bir API yaparken
      Değişiklikleri canlı DOM ağacına dönüştürüp yansıtmak yerine, açıkçası oyunlardaki gibi canvas'a piksel çizmek daha kolay bile olabilir
    • Yüz binlerce satırlık bir FX işlem uygulaması geliştiriyorum. Kalın bir masaüstü uygulamasının yerini alacak düzeyde; 20-30 geliştirici ve her birinin kendi geliştiricileri olan birden fazla müşteri var. Framework olmadan deneyin derseniz ancak bol şans dileyebilirim
    • Tamamen aynı fikirdeyim. Çok karmaşık ve etkileşimi yoğun SPA'lar da geliştirdim, ama bunların çözdüğünü söylediği problemle hâlâ karşılaşmadım
  • Promises iyi bir başarı örneği, ama async/await olmasaydı mutlaka standartlaştırılması gerekmezdi
    Mevcut taslağın Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz vb. projelerin yazarları/bakımcılarının tasarımlarına dayandığı söyleniyor; mevcut kütüphane yazarlarının bu öneriye nasıl baktığını merak ediyorum. React'in listede olmaması da ilginç
    Signals kanallara biraz benziyor, ama tek bir alıcı yerine yayın yapmasıyla farklılaşıyor. Bunu kullanıp web worker'ların onMessage callback'i yerine kanallarla iletişim kurabilmesi güzel olurdu. Özellikle Go'daki gibi signals/channels/promises üzerinde select yapılabilirse, birden çok eşzamanlı mesajlaşma mekanizmasını callback'lerle yönetmeye göre sözdizimsel bir avantaj sağlar. Örneğin signals'ı Promise.any içine dahil edebilmek gibi

    • “async/await olmasaydı standartlaştırılması gerekmezdi” görüşüne kesinlikle katılmıyorum
      x instanceof Promise doğrudan çalışmaz. Benim kütüphanemdeki then metodu catch callback'i alıyor da başka bir kütüphane almıyorsa, sessizce birlikte çalışmazlar ve bunu tespit etmenin de bir yolu yoktur. finally ne zaman çalışır? Callback'lerin ne kadar asenkron çalışacağı konusunda nasıl bir beklentiniz olabilir?
      Standart olmazsa Promise kullanan her kütüphane kendi polyfill'ini getirmek zorunda kalır. Çünkü mevcut olana güvenemez. Ayrıca başka bir kütüphanenin Promise'ini de gerçekten tüketemezsiniz. Beklediğiniz şekilde davranacağına güvenemezsiniz
      Bu bir tahmin değil; yıllarca gerçekten böyleydi ve birçok insanın katlanmak zorunda kaldığı bir cehennemdi
    • async/await'ten bağımsız standartlaşma faydaları da var. JavaScript motorları, Promise'i yoğun kullanan uygulamalara fayda sağlayan performans optimizasyonları yapabildi; standart olmasaydı bu mümkün olmazdı
    • React'in listede olmamasının nedeni, signals'ın Preact'ten farklı olarak React çekirdek API'sinin bir parçası olmaması
      Belirsiz sezgime göre signals, genelleştirilmiş useEffect()e fazla benziyor; React'e girerse render döngüsü sırasında ne olup bittiğini daha da kafa karıştırıcı hale getirebilir. İyi ya da kötü, React signals'tan farklı bir güncelleme yaklaşımı seçti. Yine de uygulanabilirliği konusunda yanılıyor olabilirim
    • React'in bu listede olmamasının nedeni, etkisinin imperatif değil deklaratif olması. Props değişimi ve yeniden render da bir seviye soyutlanmış deklaratif bir yapı olarak görülebilir. useEffect imperatif davranışı temiz biçimde izole eder
      Bu, Ember veri bağlamasına çok benziyor ve sonunda imperatif bir kâbusa dönüşebilir. Varsayılan durum “ayağına sıkmaya hazır bir silah”a yakın; öyle olmamasını sağlamak için muazzam bir bilişsel yük ve meta pattern'ler gerekir
    • Buna “EventEmitter” demek yeterli olabilir
      https://nodejs.org/en/learn/asynchronous-work/the-nodejs-eve...
  • Bağlantı verilen README’deki örneği anlayamadım
    // A library or framework defines effects based on other Signal primitives
    declare function effect(cb: () => void): (() => void);
    Hangi kütüphane? Hangi framework? Burada koptum. effect nedir?
    effect(() => element.innerText = parity.get());
    effect, parity her değiştiğinde bu lambda’yı çağırması gerektiğini nasıl biliyor? Signal her değiştiğinde bu lambda’yı mı çağırıyor? Öyleyse önbelleğe alma konusundan neden bahsediliyor? Muhtemelen öyle değildir
    Her hâlükârda yazarların aktarmaya çalıştığı şeyi doğru anladıysam signal fikrinin kendisi mantıklı görünüyor. Ancak bu tür bir ayrık mimarinin büyük sorunu, uygulama yeterince karmaşık hâle geldiğinde belirli bir olayın neden gerçekleştiğini izlerken yolunuzu kaybetmeniz. İdeal olarak signals stack trace’i düzelterek, callback çağrıldığında en başta signal’ı tetikleyen kodun stack trace’ini zaten içermeli

    • effect adlı fonksiyonu dışa aktaran birden fazla kütüphane var ve signal güncellemelerine tepki olarak rastgele kod çalıştırmayı sağlıyorlar. Preact dokümanlarındaki signals ve effects’e giriş iyi: https://preactjs.com/guide/v10/signals#effectfn
      Anladığım kadarıyla bu tür bir effect fonksiyonu önce callback’i bir kez çalıştırarak çalışma sırasında hangi signal’lara erişildiğini kontrol ediyor ve o callback’in bağımlı olduğu signal her güncellendiğinde callback’i yeniden çağırıyor. Signal erişimi senkron ve tek iş parçacıklıysa, callback çalışırken bir signal’a erişilmiş olması, o callback’in ilgili signal’a abone olması gerektiğini bilmek için yeterli
      Getter ile de yapılabilir. effect fonksiyonunun, getter metodunda signal’ın hangi özelliğine erişildiğini izlediği bir yöntem; bildiğim kadarıyla Vue 2 geçmişte bunu kullanıyordu. Proxy ile nesne erişimi de izlenebilir. Önerideki örnekte signal değerine erişmek için çağrılan bir get metodu var ve bu metodun çalıştırılması yoluyla bağımlılıklar izlenebilir
      [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
      [2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    • parity.get() çağrısı, effect()’e geçirilen fonksiyon için bir bağımlılık kaydeder. parity güncellendiğinde o fonksiyon çağrılır
      Signal her değiştiğinde değil, yalnızca bağımlı olduğu signal değiştiğinde çağrılır
      Bu durumda parity, isEven’e bağımlıdır; isEven de counter’a bağımlıdır. Bu yüzden counter güncellendiğinde tüm bağımlılık zinciri geçersiz kılınır, parity geçersiz olur ve callback yeniden çalıştırılır
    • Signals implementasyonları, adı ne olursa olsun, genel olarak dinamik bir bağımlılık grafiği oluşturur ve bir düğüm okunduğunda bir kenar oluşur. Bu varsayımsal effect gibi izleme bağlamlarında, okuma signal’ın durum düğümü ile effect’in hesaplama düğümü arasında bir kenar oluşturur; bu da pratikte ikincisinin, birincideki sonraki yazmalara abone olmasını sağlayarak hesaplamanın ne zaman yeniden çalıştırılacağını belirler
    • effect, çağırmak istediğiniz rastgele bir fonksiyondur
      signals’da bağımlılık izleme mekanizması hangi değerin yeniden hesaplanması gerektiğini bilir; bunun sonucunda sistem hangi fonksiyonun yeniden çağrılması gerektiğini de bilir
    • watcher’ın effect implementasyonu için gerekli olacağını düşünüyorum
  • Bununla ilgili olarak S.js var: https://github.com/adamhaile/s
    signals’ı seviyorum. UI oluştururken diğer tüm temel öğelere tercih ederim; belki tek istisna cassowary kısıt algoritması olabilir. Eğlence için kullandığım her dilde signals’ı taklit etmeye çalışıyorum
    Ama bunun JavaScript dilinin kendisine girecek bir şey olduğunu hiç düşünmüyorum. Dilin bir süre rahat bırakılmasını isterim. İnsanlar zaten takip etmekte zorlanıyor ve TC-39 insanları şimdiden dilden ürkütüp uzaklaştırıyor

  • Bu, en sevdiğim JS effect sistemi olan MobX’e çok benziyor
    MobX sürümü şöyle
    import { observable, computed, autorun } from 'mobx';
    const counter = observable.box(0);
    const isEven = computed(() => (counter.get() & 1) === 0);
    const parity = computed(() => isEven.get() ? "even" : "odd");
    autorun(() => { element.innerText = parity.get(); });
    setInterval(() => counter.set(counter.get() + 1), 1000);

    • MobX gerçekten signals’tır. Sadece bağımlılıkları getter ile açıkça izlemek yerine proxy nesneleri üzerinden örtük olarak izleyen signals’tır
  • “Şu sıralar kullandığım framework’ü standart kütüphaneye gömelim!” havasında
    Kız arkadaşının adını vücuduna dövme yaptırmaya benziyor

    • Bu öyle bir şey değil
      Çoğu framework’ün kullanmak üzere yakınsadığı yapı taşını standart kütüphaneye koymak
      Promises da yaygın biçimde kullanıldıktan sonra standart kütüphaneye girdi; bu da ona benziyor