Seriya: JavaScript Basics Dars 85

IndexedDB

IndexedDB nima, localStorage'dan qanday farq qiladi, Database/Object Store/Index/Transaction tushunchalari, onupgradeneeded lifecycle, transaction ACID kafolatlari, Object Store metodlari, Index va Cursor, va brauzer engine darajasida (Storage process, LevelDB/SQLite) qanday ishlashi.

85-dars

IndexedDB

localStorageni eslang: u sinxron ishlaydi — ya'ni localStorage.setItem() chaqirilganda, main thread to'xtaydi, disk operatsiyasi tugaguncha kutadi. Bundan tashqari u faqat string saqlay oladi (JSON.stringify qilib), va hajmi ~5-10MB bilan cheklangan.

IndexedDB — bu butunlay boshqa darajadagi yechim: brauzer ichidagi to'liq, asinxron, tranzaksion NoSQL ma'lumotlar bazasi. U:

  • Obyektlarni to'g'ridan-to'g'ri (Structured Clone orqali, JSON.stringifysiz) saqlaydi
  • Asinxron — main threadni bloklamaydi
  • Ko'p gigabaytgacha hajmni qo'llab-quvvatlaydi (diskka bog'liq)
  • Service Worker ichida ham ishlaydi (chunki localStorage u yerda mavjud emas — buni eslaysiz)
  • Indekslar orqali tez qidiruv imkonini beradi (nomidan ko'rinib turibdi)

1. Asosiy tuzilma — 4 ta tushuncha

Tushuncha Nima bu
Database Butun bazaning o'zi, nomi va versiyasi bor
Object Store SQL'dagi "jadval"ga o'xshaydi — obyektlar shu yerda saqlanadi
Index Object Store ichidagi biror maydon bo'yicha tez qidirish uchun qo'shimcha struktura
Transaction Bir yoki bir nechta operatsiyani atomik guruhlashning yagona yo'li
const request = indexedDB.open('MyStore', 1); // (nomi, versiyasi)

request.onupgradeneeded = function (event) {
  const db = event.target.result;

  // faqat shu yerda — struktura o'zgartiriladi (jadval yaratish)
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('byEmail', 'email', { unique: true });
};

request.onsuccess = function (event) {
  const db = event.target.result;
  console.log('Baza ochildi', db);
};

request.onerror = function (event) {
  console.error('Xato:', event.target.error);
};

Diqqat qiling — bu Promise emas, balki eski uslubdagi event-based API (IndexedDB fetchdan oldin, 2010'larning boshida standartlashtirilgan, shuning uchun sintaksisi eskirgan ko'rinadi). Zamonaviy loyihalarda odatda idb kabi kutubxona bilan Promise'ga o'raladi, lekin mexanizmni tushunish uchun xom API'ni ko'ramiz.


2. Nega onupgradeneeded alohida event

Bu — IndexedDB'ning eng muhim arxitektura qarori: struktura o'zgarishi (jadval yaratish, indeks qo'shish) va data bilan ishlash (yozish, o'qish) ataylab ajratilgan.

indexedDB.open('MyStore', 1) chaqirilganda:

Agar baza UMUMAN mavjud bo'lmasa
    → versiya 0'dan 1'ga "upgrade" hisoblanadi → onupgradeneeded ishga tushadi

Agar baza mavjud, lekin eski versiyada (masalan, browser'da v1 bor, kod v2 so'ramoqda)
    → onupgradeneeded yana ishga tushadi, event.oldVersion / event.newVersion beriladi

Agar versiya bir xil
    → to'g'ridan-to'g'ri onsuccess

createObjectStore faqat onupgradeneeded ichida chaqirilishi mumkin — boshqa joyda chaqirsangiz, xato beradi. Bu qoida sabab: brauzer struktura o'zgarishini maxsus, qisqa muddatli tranzaksiya ichida, boshqa hech qanday operatsiya bo'lmayotganda amalga oshiradi — bu race condition'lardan (masalan, ikkita tab bir vaqtda struktura o'zgartirsa) himoya qiladi.


3. Transaction: nega har bir operatsiya shu orqali o'tadi

const tx = db.transaction('users', 'readwrite'); // (qaysi store, rejim)
const store = tx.objectStore('users');

store.add({ id: 1, name: 'Ali', email: 'ali@mail.com' });

tx.oncomplete = () => console.log('Tranzaksiya tugadi');
tx.onerror = () => console.error('Xato:', tx.error);

Bu yerda nima uchun store.add() to'g'ridan-to'g'ri emas, balki avval transaction ochib, keyin undan store olish kerak — bu IndexedDB'ning ACID (Atomicity, Consistency, Isolation, Durability) kafolatini ta'minlash usuli:

  • Atomicity: agar transaction ichida 5 ta add() bo'lsa-yu, 3-chisi xato bersa — barchasi bekor qilinadi (rollback), yarim-yo'lda qolgan holat bo'lmaydi.
  • Isolation: bir vaqtning o'zida ikkita transaction bir xil store'ga yozsa, ular bir-biriga aralashmaydi — brauzer ularni navbatga qo'yadi (agar ikkalasi readwrite bo'lsa).

readonly vs readwrite — bu farq ham muhim: agar faqat o'qiyotgan bo'lsangiz, readonly ishlatish kerak, chunki brauzer bir vaqtda ko'p readonly transaction'larni parallel o'tkazishga ruxsat beradi, lekin readwrite — navbat bilan, ketma-ket (chunki yozish paytida boshqa yozuv aralashib ketmasligi kerak).


4. Object Store metodlari — jadval

Metod Vazifasi
store.add(value, key?) Yangi yozuv qo'shadi. Agar shu key (yoki keyPath) allaqachon mavjud bo'lsa — xato beradi.
store.put(value, key?) Yozadi — agar mavjud bo'lsa ustidan yozadi (upsert), bo'lmasa yangi yaratadi.
store.get(key) Berilgan kalit bo'yicha bitta yozuvni qaytaradi.
store.getAll(query?, count?) Barcha (yoki filtrlangan) yozuvlarni massiv qilib qaytaradi.
store.delete(key) Berilgan kalitdagi yozuvni o'chiradi.
store.clear() Store ichidagi hamma narsani o'chiradi.
store.count(query?) Yozuvlar sonini qaytaradi.
store.index(name) Oldindan yaratilgan indeksga murojaat qilish uchun.
store.openCursor(range?, direction?) Katta hajmdagi data bo'ylab bir-bir, oqim tarzida yurish uchun.

Har bir metod IDBRequest qaytaradi — bu Promise emas, balki onsuccess/onerror eventlariga ega alohida obyekt (shuning uchun zamonaviy kodda idb kutubxonasi bilan awaitga o'raladi).


5. Index — nega kerak

Object Store'da standart qidiruv faqat keyPath (masalan, id) bo'yicha tez ishlaydi. Agar email bo'yicha qidirmoqchi bo'lsangiz, indekssiz — brauzer hamma yozuvni ketma-ket ko'rib chiqadi (O(n)).

store.createIndex('byEmail', 'email', { unique: true });

// keyin
const tx = db.transaction('users', 'readonly');
const index = tx.objectStore('users').index('byEmail');
const request = index.get('ali@mail.com'); // ichkarida B-Tree orqali tez qidiradi

Ichki mexanizm darajasida — index aslida alohida B-Tree struktura, u email → id xaritasini saqlab boradi, va har safar add/put chaqirilganda avtomatik yangilanadi. Bu — SQL bazalaridagi indeks tushunchasi bilan aynan bir xil.


6. Cursor — katta data bilan ishlash

Agar getAll() bilan 1 million yozuvni bir vaqtda olsangiz, butun natija xotiraga yuklanadi — bu xavfli. Cursor esa yozuvlarni bittalab, oqim (stream) tarzida o'tadi:

const request = store.openCursor();

request.onsuccess = function (event) {
  const cursor = event.target.result;
  if (cursor) {
    console.log(cursor.key, cursor.value);
    cursor.continue(); // keyingi yozuvga o'tish
  } else {
    console.log('Tugadi');
  }
};

Bu — SQL'dagi "cursor" tushunchasiga to'g'ridan-to'g'ri o'xshaydi: har bir cursor.continue() chaqiruvi navbatdagi yozuvni asinxron oladi, xotirada faqat bitta yozuv bir vaqtda saqlanadi.


7. Chuqurroq — engine darajasida qanday ishlaydi

Bu yerda muhim tushunish kerak bo'lgan narsa: IndexedDB — V8'ning o'zi ichida amalga oshirilmagan. V8 — faqat JS'ni bajaradi (parse, compile, execute), lekin IndexedDB — brauzerning storage qatlami (Chrome'da bu — LevelDB yoki yangi versiyalarda LevelDBning o'rnini bosgan SQLite ustiga qurilgan, Chromium loyihasida content/browser/indexed_db qismida C++ da amalga oshirilgan).

Oqim quyidagicha:

JS kod (V8 ichida)
    → store.add(obj) chaqiradi
    → V8 obj'ni Structured Clone orqali serializatsiya qiladi (Worker'dagi bilan bir xil algoritm!)
    → bu binary data IPC (Inter-Process Communication) orqali
      brauzerning Browser Process'iga (yoki Chrome'da alohida Storage process'iga) yuboriladi
    → u yerda LevelDB/SQLite'ga jismonan diskka yoziladi
    → tasdiqlangach, IPC orqali javob (onsuccess event) V8'ga qaytariladi

Shuning uchun IndexedDB operatsiyalari hech qachon sinxron bo'la olmaydi — bu shunchaki API dizayni emas, balki jarayonlararo (process) chegara kesib o'tishning tabiiy natijasi. Va aynan shu process chegarasi tufayli, Service Worker o'chib-yonganda ham (V8 konteksti tozalanganda ham) — IndexedDB'dagi data saqlanib qoladi, chunki u V8 heap'ida emas, balki alohida, mustaqil storage process'ida yashaydi.


8. localStorage bilan yakuniy taqqoslash

localStorage IndexedDB
Ishlash rejimi Sinxron (thread bloklanadi) Asinxron
Data turi Faqat string Har qanday obyekt (Structured Clone)
Hajm chegarasi ~5-10MB Odatda GB'lab (diskka bog'liq)
Qidiruv Yo'q (faqat key bo'yicha) Index orqali tez qidiruv
Service Worker'da ishlaydimi ❌ Yo'q ✅ Ha
Tranzaksiya (ACID) Yo'q ✅ Bor