Ders 04 / 14
Dinamik İçe Aktarım
Söz döndüren içe aktarma ifadesi, hesaplanmış belirteçle koşullu yükleme, modül kaydının tek değerlendirme güvencesi ve kod bölmenin maliyet dengesi.
İçindekiler
Önceki ders, eşzamanlı bir çağrının zamana yayılan bir yüklemeyi bekleyemeyeceğini gösterip hata iletisinin işaret ettiği çözümü sonraya bırakmıştı. Bu ders o çözümü ele alır: söz (promise) döndüren, ifade konumunda kullanılabilen ve belirteci çalışma anında hesaplanabilen bir içe aktarma biçimi.
Biçimin sağladığı esneklik, iki modül sisteminin özelliklerini birleştirir: statik biçimin belirlenimci çözümlemesi ile çağrı tabanlı biçimin koşulluluğu. Karşılığında yeni bir sorumluluk getirir — yükleme artık zamana yayılmış bir iştir ve başarısız olabilir.
İfade Olarak İçe Aktarma
Dinamik içe aktarım, sonucu bir söz olan bir ifadedir. Fonksiyon gövdesinde, koşul içinde, döngüde — programın her yerinde kullanılabilir ve her iki modül sisteminden çağrılabilir. Sözün çözüldüğü değer, önceki derste tanıtılan ad alanı nesnesidir.
// dosya: bekleyen.mjs const gecikme = await new Promise((coz) => setTimeout(() => coz('hazır'), 10)); export const durum = gecikme;
// dosya: bekleyen-cagri2.cjs — bekleyen.mjs yukarıdaki dosyadır import('./bekleyen.mjs').then((m) => { console.log('eşzamansız yükleme sonucu:', m.durum); }); console.log('çağrı hemen döndü');
$ node bekleyen-cagri2.cjs çağrı hemen döndü eşzamansız yükleme sonucu: hazır
Önceki derste eşzamanlı çağrıyla yüklenemeyen modül, bu biçimle yüklendi. Çıktı sırası mekanizmayı açıklar: çağrı hemen döndü, gövde çalışmaya devam etti, modül hazır olduğunda geri çağrı yürütüldü. Eşzamansız JavaScript kursunda kurulan söz modeli burada modül yüklemeye uygulanmıştır.
Sözdizim bir fonksiyon çağrısına benzer ama fonksiyon değildir: başka bir ada atanamaz, argümansız çağrılamaz. Belirteci ise sabit olmak zorunda değildir.
Hesaplanmış Belirteç
Ölçüm kütüphanesi sonucu farklı biçimlerde sunabilir. Her biçim ayrı bir modüldür ve hangisinin kullanılacağı çalışma anında belirlenir:
// dosya: bicimlendirici/duz.mjs console.log('duz.mjs yüklendi'); export function bicimlendir(olcum) { return `sözcük: ${olcum.sozcukSayisi}, cümle: ${olcum.cumleSayisi}`; }
// dosya: bicimlendirici/tablo.mjs console.log('tablo.mjs yüklendi'); export function bicimlendir(olcum) { return Object.entries(olcum) .map(([anahtar, deger]) => `${anahtar.padEnd(16)}${deger}`) .join('\n'); }
// dosya: rapor.mjs — yukarıdaki iki biçimlendirici dosyasını kullanır const olcum = { sozcukSayisi: 8, cumleSayisi: 2 }; async function rapor(bicim) { const modul = await import(`./bicimlendirici/${bicim}.mjs`); return modul.bicimlendir(olcum); } console.log(await rapor('duz')); console.log('---'); console.log(await rapor('tablo')); console.log('---'); console.log(await rapor('duz'));
$ node rapor.mjs duz.mjs yüklendi sözcük: 8, cümle: 2 --- tablo.mjs yüklendi sozcukSayisi 8 cumleSayisi 2 --- sözcük: 8, cümle: 2
Üçüncü çağrıda duz.mjs yeniden yüklenmedi. Çalışma zamanı, çözülmüş her belirteci bir
modül kaydında (module registry) tutar; ikinci istek kayıttaki değerlendirilmiş modülü
döndürür. Önceki iki derste sayılan tek değerlendirme güvencesi, dinamik yüklemede de
geçerlidir. Kayıt anahtarı çözülmüş adrestir: aynı dosyaya iki farklı belirteçle ulaşılsa
bile tek kayıt oluşur.
Esnekliğin bedeli, önceki derste çağrı tabanlı sistem için söylenenin aynısıdır. Belirteç bir şablon dizgisiyle üretildiğinde hangi dosyanın yükleneceği kaynağa bakılarak bilinemez. Statik çözümleme yapan bir araç için bu, çizgenin kopması demektir; araç ya dizinin tamamını çıktıya katar ya da hiçbirini katamaz.
Bu nedenle, seçim sonlu bir kümedense belirteci sabit tutan biçim yeğlenir:
// dosya: sabit.mjs — yukarıdaki iki biçimlendirici dosyasını kullanır const kosul = process.argv[2] === 'tablo'; const modul = kosul ? await import('./bicimlendirici/tablo.mjs') : await import('./bicimlendirici/duz.mjs'); console.log(modul.bicimlendir({ sozcukSayisi: 8, cumleSayisi: 2 }));
$ node sabit.mjs duz.mjs yüklendi sözcük: 8, cümle: 2
Davranış aynıdır, ama iki belirteç de sabittir. Araç her iki dosyayı da görebilir ve ayrı parçalar olarak işaretleyebilir.
Yükleme Başarısız Olabilir
Deyim biçiminde eksik bir modül, program başlamadan hata verirdi. Dinamik biçimde yükleme çalışma anında gerçekleşir; bu yüzden başarısızlık, yakalanabilir bir reddetmedir:
// dosya: eksik.mjs try { await import('./bicimlendirici/yok.mjs'); } catch (hata) { console.log('yakalandı:', hata.code); } console.log('program sürüyor');
$ node eksik.mjs yakalandı: ERR_MODULE_NOT_FOUND program sürüyor
Program çökmedi. Bu, isteğe bağlı yeteneklerin yüklenmesinde yararlı bir davranıştır: bir eklenti bulunamadığında ana işlev çalışmaya devam edebilir. Aynı davranış, bir sorumluluk da doğurur — zorunlu bir bağımlılık dinamik olarak yükleniyorsa, eksikliği çalışma anına kadar fark edilmez. Bağlama zamanı ile hata görme zamanı arasındaki ilişki, Bilgisayarlar Nasıl Çalışır kursundaki bağlama ve yükleme dersinde kurulan ilişkinin aynısıdır.
Ağ üzerinden yüklenen bir modülde reddetmenin nedeni yalnızca eksik dosya değildir: kesilen bağlantı, zaman aşımı ve hatalı yanıt da aynı yola çıkar. Bu yüzden dinamik yükleme çağrıları, Eşzamansız JavaScript kursunda tanıtılan yeniden deneme ve geri çekilme kalıplarının uygulanabileceği noktalardır.
Çözülen Değerin Yapısı
Sözün çözüldüğü ad alanı nesnesi, deyim biçimindekiyle aynı nesnedir; varsayılan dışa
aktarım orada default adıyla durur. Çağrı tabanlı bir modül dinamik olarak yüklendiğinde
bu nesnenin içeriği, önceki derste anlatılan ad çıkarımının sonucudur:
// dosya: eski-bicim.cjs function bicimle(sayi) { return sayi.toFixed(2); } module.exports = { bicimle, surum: '1.0.0' };
// dosya: dinamik-cjs.mjs — eski-bicim.cjs yukarıdaki dosyadır const m = await import('./eski-bicim.cjs'); console.log('ad alanı anahtarları:', Object.keys(m)); console.log('varsayılan:', m.default.surum, '| adlandırılmış:', m.surum);
$ node dinamik-cjs.mjs ad alanı anahtarları: [ 'bicimle', 'default', 'module.exports' ] varsayılan: 1.0.0 | adlandırılmış: undefined
Çıkarım bicimle adını buldu, surum adını bulamadı: biri kısaltılmış özellik yazımıyla
bir bildirime bağlanmıştı, diğeri doğrudan bir dizgi değeriydi. Sonuç, çağrı tabanlı bir
modülden ad alırken güvenilir tek yolun default üzerinden geçmek olduğudur. Anahtar
listesindeki üçüncü ad çalışma zamanının eklediği bir kolaylıktır ve taşınabilir değildir.
Yükleme sözü bir değere de bağlanabilir; bu, bir modülü ilk gerektiğinde yükleyip sonraki çağrılarda aynı sözü paylaşan tembel başlatma kalıbını verir:
// dosya: tembel.mjs — bicimlendirici/tablo.mjs yukarıda tanımlanan modüldür let bekleyen; function bicimlendiriciAl() { bekleyen ??= import('./bicimlendirici/tablo.mjs'); return bekleyen; } const [a, b] = await Promise.all([bicimlendiriciAl(), bicimlendiriciAl()]); console.log('aynı söz mü:', bicimlendiriciAl() === bekleyen); console.log('aynı ad alanı mı:', a === b); console.log(a.bicimlendir({ sozcukSayisi: 8 }));
$ node tembel.mjs tablo.mjs yüklendi aynı söz mü: true aynı ad alanı mı: true sozcukSayisi 8
Kod Bölme
Dinamik içe aktarımın araç düzeyindeki karşılığı kod bölmedir (code splitting). Bir paketleyici, dinamik içe aktarım gördüğü yerde çizgeyi keser ve o dalı ayrı bir çıktı dosyasına — bir parçaya (chunk) — koyar. Ana çıktı yalnızca parçanın adresini taşır; dosya ilk gerektiğinde istenir.
Kazanç, ilk yükleme maliyetinin küçülmesidir. Bir uygulamanın nadiren açılan bölümü, hiç açılmadığında hiç indirilmez. Ölçüm kütüphanesinde tablo biçimlendiricisi buna örnektir: çoğu çağrı düz biçimi kullanır.
Maliyet üç kalemdir:
Ek gidiş dönüş. Parça, gerektiği anda ayrıca istenir. Kullanıcının beklediği an, ölçümün en pahalı olduğu andır.
Yinelenen kod. İki parça aynı yardımcı modülü kullanıyorsa, araç ya kodu iki parçaya da kopyalar ya da üçüncü bir ortak parça üretir. İkisi de karmaşıklık ekler.
Hata yolu. Her parça bir başarısızlık noktasıdır; her dinamik yükleme çağrısı bir hata işleyicisi gerektirir.
Bu nedenle bölme, her dinamik içe aktarımın otomatik sonucu değil, ölçüme dayalı bir karardır. Bölmenin kazandırdığı süre, ek isteğin maliyetinden büyük olmalıdır. Sık kullanılan bir kod yolunu bölmek başarımı düşürür.
Ne Zaman Kullanılmamalı
Dinamik biçim, deyim biçiminin yerini almaz. Modülün her çalıştırmada ve hemen gerektiği durumlarda deyim biçimi doğru seçimdir: bağımlılık kaynağa yazılmış olur, eksikliği program başlamadan yakalanır ve araçlar çizgeyi görebilir.
Dinamik biçim üç durumda yerindedir: modül yalnızca belirli bir koşulda gerekiyorsa, hangi modülün gerekeceği çalışma anında belirleniyorsa, ya da modülün yüklenmesi ilk açılışın dışına ertelenebiliyorsa. Bunların dışında kullanıldığında, kazanç sağlamadan çizgeyi araçlardan gizler.
Özet
- Dinamik içe aktarım söz döndüren bir ifadedir; her konumda ve her iki modül sisteminden çağrılabilir, sözün çözüldüğü değer ad alanı nesnesidir.
- Belirteç çalışma anında hesaplanabilir; hesaplanmış belirteç statik çözümlemeyi imkânsızlaştırdığı için sonlu seçimlerde sabit belirteçli biçim yeğlenir.
- Modül kaydı, çözülmüş adres başına tek değerlendirme güvencesini dinamik yüklemede de korur.
- Yükleme başarısızlığı yakalanabilir bir reddetmedir; bu, isteğe bağlı yetenekler için kazanç, zorunlu bağımlılıklar için gecikmiş hata anlamına gelir.
- Kod bölme ilk yükleme maliyetini düşürür; karşılığında ek istek, yinelenen kod ve yeni hata yolları getirir, bu yüzden ölçüme dayalı bir karardır.
Sonraki Adım
Buraya kadar tüm belirteçler göreli dosya yollarıydı. Oysa gerçek projelerde içe aktarımların
çoğu metin-olcer gibi çıplak adlardır ve bu adların hangi dosyaya karşılık geldiği dosya
sisteminde yazılı değildir. Sonraki konu bu çözümlemeyi ele alır: bir paketin bildirim
dosyası, çıplak adın aranma kuralı ve kurulu ağacın kilit dosyasıyla sabitlenmesi.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.