İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat