Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 1.1 Ochilgan narsa yopilishi kerak
- 1.2 G'oya
- 2. Symbol.dispose: "meni qanday yopish kerak"
- 2.1 Protokol
- 2.2 using = try...finally
- 3. Bir nechta resurs: teskari tartib
- 4. using qoidalari
- 4.1 const ga o'xshaydi
- 4.2 Qiymat — yopiladigan obyekt yoki null
- 4.3 Qayerda yozish mumkin
- 5. Node 24 dagi tayyor resurslar
- 6. DisposableStack: hamma narsani yopishga qo'shish
- 6.1 Muammo: yopish — oddiy funksiya
- 6.2 Vaqtinchalik obuna
- 6.3 Tartib va move
- 7. Ikki xato birdaniga: SuppressedError
- 8. await using: asinxron yopish
- 9. Bugun qayerda ishlaydi — halol ro'yxat
- 10. Ko'p uchraydigan xatolar
- 10.1 Oddiy const bilan "yopiladigan" obyekt
- 10.2 Metod nomi dispose, simvol emas
- 10.3 Resursni blokdan tashqariga uzatish
- 10.4 Brauzer kodida tekshirmasdan ishlatish
- 10.5 [Symbol.dispose] ichida xato tashlash
- 11. Mashqlar
- 1-mashq (oson): Stol band
- 2-mashq (o'rta): Ikki stol va xato
- 3-mashq (qiyin): DisposableStack bilan tozalash ro'yxati
- 4-mashq: Vazifalar qadami — vaqtinchalik jurnal (mashqlar repo)
- 12. Real ishda
- Xulosa
- Manbalar
JavaScript using: resurslarni aniq boshqarish va Symbol.dispose
Qisqacha:
using nom = resurs;—constga o'xshash e'lon, faqat blok tugaganda JavaScript resursning[Symbol.dispose]()metodini o'zi chaqiradi: xato chiqsa ham,returnbo'lsa ham. Butry...finallydagi "yopishni unutmang" ishini avtomatlashtiradi. Bir nechtausingteskari tartibda yopiladi. Node 24, Chrome 134+ va Firefox 141+ da ishlaydi, Safari'da hali yo'q — Baseline emas. Shuning uchun bugun uni Node kodida ishlatamiz.
Bu darsda
- "Resurs" nima ekanini va uni yopish nega muhimligini tushuntira olasiz.
Symbol.disposebilan o'z "yopiladigan" obyektingizni yasaysiz va uniusingbilan ishlatasiz.- Bir nechta resurs qaysi tartibda yopilishini va xato chiqqanda nima bo'lishini (
SuppressedError) bilasiz. DisposableStackbilan oddiy funksiyalarni ham avtomatik tozalashga qo'shasiz.usingbugun qayerda ishlaydi, qayerda yo'q — halol ro'yxatni bilasiz.
Oldin bilishingiz kerak: try / catch / finally, Xato strategiyasi, Well-known symbols, JS'da dizayn naqshlari.
1. Nega bu kerak?
1.1 Ochilgan narsa yopilishi kerak
«Bahor» kassasida har kuni ertalab Otabek smena ochadi, kechqurun yopadi. Yopilmagan smena — muammo: ertangi kun hisobi kechagisi bilan aralashadi. Dasturda ham shunday narsalar ko'p. Resurs (resource) — dastur vaqtincha egallab oladigan va ishi tugagach albatta qaytarishi (yopishi) kerak bo'lgan narsa. Xuddi kutubxonadan olingan kitob kabi: o'qib bo'lgach, qaytarasiz. Misollar:
- ochilgan fayl — yopilmasa, boshqa dastur unga yoza olmaydi;
- taymer (
setInterval) — to'xtatilmasa, abadiy ishlaydi va Node dasturi tugamaydi; - hodisaga obuna — bekor qilinmasa, kerak bo'lmagan funksiya chaqirilaveradi (JS'da dizayn naqshlari darsidagi
obunaaynan bekor qilish funksiyasini qaytarardi); - vaqtinchalik papka — o'chirilmasa, disk to'lib boradi.
Qolip doim bir xil: och → ishlat → yop. try / catch / finally darsida "yop" qismini finally ga qo'yishni o'rgandik — xato bo'lsa ham yopilsin deb:
function smenaOch(kassir) {
console.log(`${kassir}: smena ochildi`);
return { yop: () => console.log(`${kassir}: smena yopildi`) };
}
const smena = smenaOch("Otabek");
try {
console.log("Chek: osh, 35 000");
JSON.parse("{");
} catch {
console.log("Buzuq buyurtma o'tkazib yuborildi");
} finally {
smena.yop();
}Konsolda:
Otabek: smena ochildi
Chek: osh, 35 000
Buzuq buyurtma o'tkazib yuborildi
Otabek: smena yopildiIshlaydi. Lekin uchta zaif joyi bor:
- Unutish oson.
finally— dasturchining xotirasiga tayanadi. Bitta joyda unutildi — resurs "oqib ketdi". - "Och" va "yop" bir-biridan uzoq. Orada 30 qator kod bo'lsa, ular juft ekanini ko'rish qiyin.
- Ko'p resurs — ko'p qavat. Ikki resurs — ichma-ich ikki
try...finally, uchtasi — uch qavat. Kod o'ngga "qochib" ketadi.
1.2 G'oya
Mehmonxonadagi xona kartasini eslang. Eshik yonida karta uchun teshik bor. Kartani qo'ysangiz — chiroq yonadi. Chiqib ketayotganda kartani olsangiz, chiroq va konditsioner o'zi o'chadi. Siz "chiroqni o'chirishni unutmang" deb o'ylamaysiz — tizim buni chiqish bilan bog'lab qo'ygan.
using — const va let kabi yangi e'lon kalit so'zi. U ham shunday ishlaydi: resurs blokka ({ } orasidagi kodga) bog'lanadi. Blokdan chiqish — qanday yo'l bilan bo'lmasin — resursni yopadi. Xona kartasi kabi, buni ham o'zimiz eslab qolishimiz shart emas.
2. Symbol.dispose: "meni qanday yopish kerak"
2.1 Protokol
using bilan ishlatiladigan obyekt bitta shartga javob berishi kerak: uning [Symbol.dispose] nomli metodi bo'lsin. Bu metod — "meni yop".
Symbol.dispose — Well-known symbols darsidagi Symbol.iterator ning qarindoshi. Symbol.iterator "meni qanday aylanish kerak" deydi va for...of uni ishlatadi. Symbol.dispose "meni qanday yopish kerak" deydi va using uni ishlatadi. Bunday obyektni yopiladigan (disposable) obyekt deyishadi.
function smenaOch(kassir) {
console.log(`${kassir}: smena ochildi`);
return {
kassir,
[Symbol.dispose]() {
console.log(`${kassir}: smena yopildi`);
},
};
}
{
using smena = smenaOch("Otabek");
console.log(`Chek: osh, kassir ${smena.kassir}`);
}
console.log("Blokdan chiqdik");Konsolda:
Otabek: smena ochildi
Chek: osh, kassir Otabek
Otabek: smena yopildi
Blokdan chiqdik[Symbol.dispose]() { ... } — kalit simvol bo'lgan metod (Shorthand va computed kalitlar). Biz uni hech qayerda chaqirmadik — } ga yetganda JavaScript chaqirdi.
2.2 using = try...finally
using hech qanday "sehr" emas. Yuqoridagi blok mohiyatan shuning qisqa yozuvi:
{
const smena = smenaOch("Otabek");
try {
console.log(`Chek: osh, kassir ${smena.kassir}`);
} finally {
smena[Symbol.dispose]();
}
}Ya'ni finally ning hamma kafolati using da ham bor: xato chiqsa ham, return bo'lsa ham yopiladi.
function smenaOch(kassir) {
return {
[Symbol.dispose]() {
console.log(`${kassir}: smena yopildi`);
},
};
}
function chekUr(matn) {
using smena = smenaOch("Otabek");
const buyurtma = JSON.parse(matn);
return `Chek: ${buyurtma.taom}`;
}
console.log(chekUr('{"taom": "Manti"}'));
try {
chekUr("{");
} catch (xato) {
console.log("Ushlandi:", xato.name);
}Konsolda:
Otabek: smena yopildi
Chek: Manti
Otabek: smena yopildi
Ushlandi: SyntaxErrorIkkala chaqiruvda ham smena yopildi. Birinchisida — return dan oldin (xuddi finally dagidek — natija qaytishidan oldin). Ikkinchisida — xato yuqoriga ketishidan oldin. Funksiya tanasi ham blok: using funksiya tugaganda ishlaydi.
Tekshirib ko'ring:
usingblok ichida yozilgan. Yopish qachon chaqiriladi —usingqatoridan keyinmi yoki boshqa vaqtda?
Javob
Blok tugaganda — } ga yetganda yoki blokdan return, break yoki xato bilan chiqilganda. using qatorida resurs faqat ro'yxatga olinadi. Shuning uchun resurs blokning oxirigacha ishlatilishi mumkin.
3. Bir nechta resurs: teskari tartib
Choyxona ochilishi: avval kassa, keyin oshxona. Yopilishi-chi? Mantiqan teskari: avval oshxona (u kassaga hisobot beradi), keyin kassa. using aynan shunday qiladi — oxirgi ochilgan birinchi yopiladi:
"Stek" ustuniga qarang: resurslar xuddi call stack kabi ustma-ust yig'iladi va ustidan olinadi. Nega bu to'g'ri tartib? Ko'pincha keyingi resurs oldingisiga tayanadi: oshxona kassaga yozadi, fayl papka ichida turadi. Avval tayanganni yopish kerak, keyin tayanchni. Ichma-ich try...finally ham aynan shu tartibni beradi — using uni avtomatik saqlaydi.
4. using qoidalari
4.1 const ga o'xshaydi
using — blok ichida yashaydigan, qayta tayinlab bo'lmaydigan e'lon:
{
using smena = { [Symbol.dispose]() {} };
smena = null;
}TypeError: Assignment to using variable.Tarjimasi: "using o'zgaruvchisiga qiymat berish". Mantiqiy: agar qayta tayinlash mumkin bo'lsa, JavaScript qaysi obyektni yopishni bilmay qolardi. E'tibor bering: bu SyntaxError emas, TypeError — xato kod bajarilayotganda chiqadi, const dagi Assignment to constant variable. kabi (Stack trace).
4.2 Qiymat — yopiladigan obyekt yoki null
using ga oddiy son yoki [Symbol.dispose] siz obyekt berilsa — xato. Node 24 dagi haqiqiy xabarlar:
TypeError: An object is expected with `using` declarations
TypeError: Symbol(Symbol.dispose) is not a functionBirinchisi — using x = 5: "using e'lonida obyekt kutilgan". Ikkinchisi — using x = {}: obyekt bor, lekin [Symbol.dispose] funksiya emas (aslida — yo'q, undefined).
null va undefined esa ruxsat etilgan — ular uchun yopish shunchaki o'tkazib yuboriladi. Bu "resurs bo'lishi ham mumkin, bo'lmasligi ham" holati uchun qulay:
function jurnalOch(kerakmi) {
if (!kerakmi) {
return null;
}
return { [Symbol.dispose]: () => console.log("jurnal yopildi") };
}
{
using jurnal = jurnalOch(false);
console.log("jurnalsiz ishladik");
}Konsolda:
jurnalsiz ishladik4.3 Qayerda yozish mumkin
using — blok ichida: { }, funksiya tanasi, for tanasi. Shuningdek for (using x of ...) — har aylanishda element yopiladi. Faylning eng yuqori darajasida esa faqat modulda (Nega modullar): Node'dagi .mjs fayl yoki brauzerdagi <script type="module">. Oddiy brauzer skriptining yuqori darajasida Chrome 154 uni tanimaydi:
Uncaught SyntaxError: Unexpected identifier 'smena'Qoida oddiy: using ni blok yoki funksiya ichida yozing — u hamma joyda ishlaydi va resurs qancha yashashini aniq ko'rsatadi.
5. Node 24 dagi tayyor resurslar
Node'ning ba'zi obyektlarida [Symbol.dispose] allaqachon bor — ularni o'zingiz o'rashingiz shart emas.
Diqqat: bu bo'limdagi ikki misol faqat Node 24 da ishlaydi. Saytdagi «Ishga tushir» kodni brauzerda bajaradi. U yerda
setIntervalobyekt emas, oddiy son qaytaradi vaTypeError: An object is expected withusingdeclarationschiqadi (Chrome 154 da sinadik).node:fsesa brauzerda umuman yo'q. Bu bloklarni kompyuteringizda.mjsfaylga yozib,nodebilan ishga tushiring.
Taymer. setInterval qaytargan obyekt yopilganda taymer to'xtaydi:
{
using taymer = setInterval(() => console.log("tik"), 10);
console.log("blok ichida");
}
console.log("taymer to'xtadi, dastur tugaydi");Konsolda:
blok ichida
taymer to'xtadi, dastur tugaydiBitta ham "tik" chiqmadi: blok tugaganda taymer to'xtadi. using siz bu dastur abadiy "tik" yozib turardi — clearInterval unutilgan.
Vaqtinchalik papka. Node 24 da fs.mkdtempDisposableSync vaqtinchalik papka yaratadi. Yopilganda u papkani ichidagi fayllar bilan birga o'chiradi. fs va os — Node'ning fayl tizimi va operatsion tizim modullari, import qatorlarini Nega modullar darsida o'rganamiz, hozir natijaga qarang:
import fs from "node:fs";
import os from "node:os";
let yol;
{
using papka = fs.mkdtempDisposableSync(`${os.tmpdir()}/bahor-`);
yol = papka.path;
fs.writeFileSync(`${yol}/chek.txt`, "Osh — 35 000 so'm");
console.log(fs.readdirSync(yol)); // [ 'chek.txt' ]
}
console.log(fs.existsSync(yol)); // falseBlok ichida papkada fayl bor edi. Blokdan keyin papka umuman yo'q — using uni tozaladi. Sinov va vaqtinchalik hisobotlar uchun aynan shu kerak.
6. DisposableStack: hamma narsani yopishga qo'shish
6.1 Muammo: yopish — oddiy funksiya
Ko'p API'larda [Symbol.dispose] yo'q. "Yopish" ko'pincha oddiy funksiya: JS'da dizayn naqshlari darsidagi obuna "bekor qilish" funksiyasini qaytaradi, addEventListener uchun removeEventListener bor. Ularni using ga qanday berish mumkin?
DisposableStack — "yopishlar steki". U o'zi yopiladigan obyekt, ichiga esa istalgan tozalash ishini qo'shish mumkin:
| Metod | Nima qiladi |
|---|---|
defer(fn) |
Yopilganda fn() ni chaqiradi |
use(resurs) |
Yopiladigan obyektni qo'shadi va uni qaytaradi |
adopt(qiymat, fn) |
Yopilganda fn(qiymat) — [Symbol.dispose] siz qiymat uchun |
move() |
Hammasini yangi stekka ko'chiradi, eskisi bo'shaydi |
To'rttasini birdan yodlash shart emas. Boshida defer yetarli — keyingi misol faqat shuni ishlatadi. Qolganlari «Tartib va move» bo'limida.
6.2 Vaqtinchalik obuna
Kuzatuvchiga vaqtincha jurnal ulaymiz — faqat blok davomida:
function kuzatuvchiYarat() {
let tinglovchilar = [];
return {
obuna(tinglovchi) {
tinglovchilar.push(tinglovchi);
return () => {
tinglovchilar = tinglovchilar.filter((t) => t !== tinglovchi);
};
},
xabarBer(matn) {
tinglovchilar.forEach((t) => t(matn));
},
};
}
const royxat = kuzatuvchiYarat();
{
using stek = new DisposableStack();
stek.defer(royxat.obuna((m) => console.log("Jurnal:", m)));
royxat.xabarBer("Non olish qo'shildi");
}
royxat.xabarBer("Choy damlash qo'shildi");
console.log("Blokdan keyin jurnal jim");Konsolda:
Jurnal: Non olish qo'shildi
Blokdan keyin jurnal jimobuna bekor qilish funksiyasini qaytaradi — biz uni to'g'ridan-to'g'ri defer ga berdik. Blok tugadi → stek yopildi → bekor qilish chaqirildi → ikkinchi xabarni hech kim eshitmadi. "Obuna bo'ldim, lekin bekor qilishni unutdim" — xotira sizib chiqishining eng ko'p uchraydigan sababi (Closure tuzoqlari). DisposableStack uni blokka bog'ladi.
6.3 Tartib va move
Stekdagi ishlar ham teskari tartibda bajariladi, va bir marta yopilgan stekka yangi ish qo'shib bo'lmaydi:
const stek = new DisposableStack();
stek.defer(() => console.log("1-defer"));
stek.use({ [Symbol.dispose]: () => console.log("2-use") });
stek.adopt(35000, (narx) => console.log("3-adopt:", narx));
console.log(stek.disposed); // false
stek.dispose();
console.log(stek.disposed); // true
try {
stek.defer(() => {});
} catch (xato) {
console.log(xato.name); // ReferenceError
}Konsolda:
false
3-adopt: 35000
2-use
1-defer
true
ReferenceErrordispose() — qo'lda yopish (using siz ham ishlatsa bo'ladi). Yopilgan stekka qo'shishga urinish — ReferenceError: Cannot call DisposableStack.prototype.defer on an already-disposed DisposableStack.
move() — murakkabroq holat uchun: funksiya bir nechta resurs ochadi va ularni chaqiruvchiga topshirmoqchi. Ichkarida using stek — o'rtada xato chiqsa, hammasi yopiladi. Hammasi muvaffaqiyatli bo'lsa, return stek.move() — resurslar yangi stekka o'tadi, eski stek esa bo'sh holda yopiladi. Endi ularni yopish chaqiruvchining using iga tegishli. Bu naqsh kutubxona kodlarida uchraydi; boshida faqat defer va use yetarli.
7. Ikki xato birdaniga: SuppressedError
Yopishning o'zi ham xato tashlashi mumkin. Agar blok xato bilan tugayotgan bo'lsa-yu, yopishda yana xato chiqsa-chi? try / catch darsida ko'rdik: finally dagi throw asl xatoni bosib ketadi. using buni boshqacha hal qiladi — ikkalasini ham saqlaydi:
function smenaOch(nom) {
return {
[Symbol.dispose]() {
throw new Error(`${nom}: kassa qulfi ochilmadi`);
},
};
}
try {
using smena = smenaOch("Kassa");
throw new Error("Hisobda xato");
} catch (xato) {
console.log(xato.name);
console.log("error:", xato.error.message);
console.log("suppressed:", xato.suppressed.message);
}Konsolda:
SuppressedError
error: Kassa: kassa qulfi ochilmadi
suppressed: Hisobda xatoSuppressedError ("bosilgan xato") — ikki xatoni o'rab olgan yangi tur:
error— yangi xato, yopish paytida chiqqani;suppressed— eski xato, blokdagi asl muammo, "bosib qo'yilgan".
Hech biri yo'qolmadi. Xato strategiyasi darsidagi "xatoni yutib yubormang" qoidasini til o'zi bajaryapti. Lekin eng yaxshisi — [Symbol.dispose] ichida xato tashlamaslik: yopish imkon qadar xavfsiz bo'lsin.
8. await using: asinxron yopish
Ba'zi resurslarni yopish vaqt oladi: faylga oxirgi ma'lumotni yozish, serverdagi ulanishni yopish. Buning uchun juft vositalar bor: Symbol.asyncDispose va await using. Ular asinxron kod bilan ishlaydi — await va Promise ni 11-qismda o'rganamiz, hozir bilish shart emas. Shaklini ko'rib qo'ying:
await using fayl = await fs.promises.open("chek.txt", "w");
await fayl.write("Osh — 35 000 so'm");
// blok tugaganda fayl o'zi yopiladiNode 24 da fayl ochganda qaytadigan obyektda [Symbol.asyncDispose] bor, shuning uchun u to'g'ridan-to'g'ri await using bilan ishlaydi. Asinxron resurslar uchun stek ham bor — AsyncDisposableStack.
9. Bugun qayerda ishlaydi — halol ro'yxat
using — eng yangi imkoniyatlardan biri. Uni ishlatishdan oldin muhitni biling.
Standart. JavaScript standartini TC39 qo'mitasi yozadi (JavaScript nima darsida tanishgansiz). Yangi imkoniyat u yerda taklif sifatida 0 dan 4 gacha bosqichdan o'tadi. using 4-bosqichga yetgan — ya'ni standartga qabul qilingan. TC39'ning yakunlangan takliflar ro'yxati bo'yicha u ES2027 nashriga kiradi.
Biz o'zimiz sinab ko'rdik (2026-oktabr):
| Muhit | Natija |
|---|---|
| Node 24.21 | Ishlaydi, bayroqsiz: using, DisposableStack, SuppressedError |
| Node 22.17 | SyntaxError: Unexpected identifier — ishlamaydi |
| Chrome 154 | Ishlaydi: blokda va modulda; oddiy skriptning yuqori darajasida — yo'q |
Node 22 da V8 bayrog'i (--js-explicit-resource-management) bilan sintaksis o'qiladi, lekin bizning sinovda yopish metodi chaqirilmadi va DisposableStack yo'q edi. Ya'ni bayroq bilan ham ishonib bo'lmaydi — Node 24 kerak.
Brauzerlar (web-features 3.40, explicit-resource-management): Chrome va Edge 134 dan, Firefox 141 dan. Safari'da — yo'q. Shuning uchun Baseline holati: "Baseline emas". Telefonlarning katta qismi iPhone bo'lgan auditoriyada bu jiddiy: using li skript Safari'da SyntaxError beradi va butun fayl ishlamaydi (Stack trace darsidagi SyntaxError qoidasi).
Amaliy xulosa:
- Node kodi (server, skriptlar, testlar) — Node 24 va undan yangisida bemalol ishlating.
- Brauzer kodi — hozircha
try...finallyda qoling. Yoki kodni eski sintaksisga aylantiradigan vosita orqali o'tkazing: TypeScript 5.2 dan beriusingni tushunadi va eski brauzerlar uchuntry...finallyga aylantira oladi (TypeScript'ni 15-qismda o'rganamiz). - Safari qo'llab-quvvatlashni qo'shgach, holat o'zgaradi — web-features va MDN'dagi jadvalni tekshirib turing.
Shu sababli bu darsda vazifalar (brauzer ilovasi) ga using qo'shmaymiz — «Vazifalar qadami» mashqlar repoda, Node'da.
10. Ko'p uchraydigan xatolar
10.1 Oddiy const bilan "yopiladigan" obyekt
const smena = smenaOch() — [Symbol.dispose] bor, lekin hech kim chaqirmaydi. using ga faqat using kalit so'zi ma'no beradi. Tuzatish: using smena = ....
10.2 Metod nomi dispose, simvol emas
{ dispose() { ... } } — oddiy dispose nomli metod. using uni ko'rmaydi: TypeError: Symbol(Symbol.dispose) is not a function. Tuzatish: [Symbol.dispose]() { ... } — kvadrat qavsda simvol.
10.3 Resursni blokdan tashqariga uzatish
function papkaOl() {
using papka = fs.mkdtempDisposableSync("bahor-");
return papka; // ❌ qaytishdan oldin o'chiriladi
}Chaqiruvchi allaqachon o'chirilgan papkani oladi. Tuzatish: resursni chaqiruvchining o'zi using bilan oladi, yoki DisposableStack va move().
10.4 Brauzer kodida tekshirmasdan ishlatish
Chrome'da ishladi — demak hamma joyda ishlaydi, deb o'ylash. Safari'da butun fayl SyntaxError bilan to'xtaydi. Tuzatish: brauzer kodida yangi sintaksisdan oldin Baseline holatini tekshiring.
10.5 [Symbol.dispose] ichida xato tashlash
Yopishdagi xato asl xatoni SuppressedError ichiga o'raydi — xabar murakkablashadi. Tuzatish: yopishni xavfsiz yozing; muammo bo'lsa, console.warn bilan bildiring.
11. Mashqlar
1-mashq (oson): Stol band
stolniBandQil(raqam) funksiyasini yozing: u "N-stol band" ni chiqarsin va yopiladigan obyekt qaytarsin, yopilganda "N-stol bo'shadi" chiqsin. Blok ichida 4-stolni band qiling va "Mehmonlar ovqatlanyapti" ni chiqaring.
Yechim
function stolniBandQil(raqam) {
console.log(`${raqam}-stol band`);
return {
[Symbol.dispose]() {
console.log(`${raqam}-stol bo'shadi`);
},
};
}
{
using stol = stolniBandQil(4);
console.log("Mehmonlar ovqatlanyapti");
}Konsolda:
4-stol band
Mehmonlar ovqatlanyapti
4-stol bo'shadistol o'zgaruvchisi blok ichida ishlatilmadi — bu normal. using ning vazifasi — resursni blokka bog'lash, o'zgaruvchi esa faqat "tutqich".
2-mashq (o'rta): Ikki stol va xato
Ikki stolni band qiling (4 va 7), so'ng blok ichida xato tashlang: throw new Error("Oshxona yopiq"). Blokni try...catch ga o'rang. Konsolda nima va qaysi tartibda chiqadi? Avval o'ylab ko'ring, keyin ishga tushiring.
Yechim
function stolniBandQil(raqam) {
console.log(`${raqam}-stol band`);
return {
[Symbol.dispose]() {
console.log(`${raqam}-stol bo'shadi`);
},
};
}
try {
using birinchi = stolniBandQil(4);
using ikkinchi = stolniBandQil(7);
throw new Error("Oshxona yopiq");
} catch (xato) {
console.log("Xato:", xato.message);
}Konsolda:
4-stol band
7-stol band
7-stol bo'shadi
4-stol bo'shadi
Xato: Oshxona yopiqIkki qoida birga: teskari tartib (7, keyin 4) va "yopish — xato catch ga yetishidan oldin". try bloki ham blok — using uning oxirida ishlaydi. Yopishda xato chiqmadi — shuning uchun catch asl xatoni oldi, SuppressedError emas.
3-mashq (qiyin): DisposableStack bilan tozalash ro'yxati
Kun oxiridagi tozalash: uch ish teskari tartibda bajarilsin — "Chiroqlar o'chdi", "Kassa yopildi", "Eshik qulflandi" (eshik — eng oxirida). DisposableStack va defer bilan yozing. Bundan tashqari adopt bilan kunlik tushumni (1250000) "seyfga qo'yildi: 1250000" deb yozadigan ishni qo'shing — u kassa yopilishidan oldin bajarilsin. Qaysi tartibda qo'shish kerak?
Yechim
{
using kunOxiri = new DisposableStack();
kunOxiri.defer(() => console.log("Eshik qulflandi"));
kunOxiri.defer(() => console.log("Kassa yopildi"));
kunOxiri.adopt(1250000, (summa) => {
console.log(`Seyfga qo'yildi: ${summa}`);
});
kunOxiri.defer(() => console.log("Chiroqlar o'chdi"));
console.log("Oxirgi mehmon chiqdi");
}Konsolda:
Oxirgi mehmon chiqdi
Chiroqlar o'chdi
Seyfga qo'yildi: 1250000
Kassa yopildi
Eshik qulflandiStek teskari bajariladi, shuning uchun eng oxirida bajarilishi kerak bo'lgan ish (eshik) birinchi qo'shiladi. Qoida: ishlarni "ochilish" tartibida qo'shing — eshik ochildi, kassa ochildi, pul tushdi, chiroq yondi — yopilish o'zi teskari bo'ladi.
4-mashq: Vazifalar qadami — vaqtinchalik jurnal (mashqlar repo)
vazifalar — brauzer ilovasi, using esa Safari'da yo'q (bugungi «Bugun qayerda ishlaydi» bo'limi). Shuning uchun bu qadam kurs/mashqlar/10/59-using/jurnal.mjs da, Node 24 bilan.
JS'da dizayn naqshlari darsida VazifalarRoyxati ga obuna qo'shgan edik: u bekor qilish funksiyasini qaytaradi. Topshiriq:
- Kichik
royxatyarating:obuna(tinglovchi)(bekor qilish funksiyasini qaytaradi) vaqosh(matn)(hamma tinglovchigamatnni xabar qiladi).VazifalarRoyxatini to'liq ko'chirish shart emas. jurnalUla(royxat)funksiyasini yozing: uroyxatga "Jurnal: …" deb yozadigan tinglovchini obuna qilsin va yopiladigan obyekt qaytarsin — yopilganda obuna bekor bo'lsin va "Jurnal yopildi" chiqsin.- Blok ichida
usingbilan jurnalni ulab, ikki vazifa qo'shing. Blokdan keyin yana bitta qo'shing — u jurnalga tushmasligi kerak.
Yechim
kurs/mashqlar/10/59-using/jurnal.mjs:
function royxatYarat() {
let tinglovchilar = [];
return {
obuna(tinglovchi) {
tinglovchilar.push(tinglovchi);
return () => {
tinglovchilar = tinglovchilar.filter((t) => t !== tinglovchi);
};
},
qosh(matn) {
tinglovchilar.forEach((t) => t(matn));
},
};
}
function jurnalUla(royxat) {
const bekorQil = royxat.obuna((matn) => {
console.log(`Jurnal: «${matn}» qo'shildi`);
});
return {
[Symbol.dispose]() {
bekorQil();
console.log("Jurnal yopildi");
},
};
}
const royxat = royxatYarat();
{
using jurnal = jurnalUla(royxat);
royxat.qosh("Non olish");
royxat.qosh("Choy damlash");
}
royxat.qosh("Kitob o'qish");
console.log("Uchinchi vazifa jurnalsiz qo'shildi");Konsolda:
Jurnal: «Non olish» qo'shildi
Jurnal: «Choy damlash» qo'shildi
Jurnal yopildi
Uchinchi vazifa jurnalsiz qo'shildinode jurnal.mjs bilan ishga tushiring (Node 24). Nimaga e'tibor bering:
- "Obuna — bekor qilish" jufti endi bitta obyekt.
jurnalUlaichida bekor qilish funksiyasi closure'da saqlanadi (Closure amaliyotda),[Symbol.dispose]uni chaqiradi. Tashqi kod bekor qilishni umuman ko'rmaydi — faqat blokni. DisposableStackvarianti.jurnalUlasiz ham bo'ladi:using stek = new DisposableStack(); stek.defer(royxat.obuna(...));— «Vaqtinchalik obuna» bo'limidagidek.- Brauzerdagi ekvivalent.
vazifalar.jsda xuddi shu ishnitry...finallybilan yozish mumkin:const bekorQil = holat.vazifalar.obuna(...)vafinally { bekorQil(); }.usingu yerga Safari qo'llab-quvvatlashi paydo bo'lgach keladi.
Node 22 da ishga tushirib ko'ring: SyntaxError: Unexpected identifier 'jurnal' — versiya muhimligining amaliy isboti.
Commit (mashqlar repo): 10/59: using bilan vaqtinchalik jurnal.
12. Real ishda
- Server kodi. Ma'lumotlar bazasi ulanishi, fayl, qulf (lock) — "och va albatta yop" juftlari backend'da har qadamda. Node kutubxonalari
[Symbol.dispose]va[Symbol.asyncDispose]ni qo'shib bormoqda;usingbilan ulanishni qaytarishni unutish imkonsiz bo'ladi. - Testlar. Test uchun vaqtinchalik papka, soxta server yoki taymer — test tugashi bilan tozalanishi shart.
fs.mkdtempDisposableSynckabi API'lar aynan shu uchun qo'shilgan. Testlarni 12-qismda yozamiz. - Boshqa tillar. G'oya yangi emas: C# da
using, Python'dawith, Java'datry-with-resources. Bir tilda o'rgansangiz, boshqasida ham taniysiz — Python kursiga o'tsangiz,with open(...)aynan shu. - Intervyu. Hozircha kam so'raladi, lekin "resurs oqishi nima va uni qanday oldini olasiz?" savolida
try...finallyvausingni tilga olish — kuchli javob.
Xulosa
- Resurs — ochilgan va albatta yopilishi kerak bo'lgan narsa: fayl, taymer, obuna, vaqtinchalik papka.
using x = resurs— blok tugagandax[Symbol.dispose]()chaqiriladi: xato vareturnda ham. Mohiyatan —try...finally.- Bir nechta
using— teskari tartibda yopiladi; qiymat — yopiladigan obyekt,nullyokiundefined; qayta tayinlab bo'lmaydi. DisposableStack— oddiy funksiyalarni (defer), obyektlarni (use) va qiymatlarni (adopt) yopishga qo'shadi.- Blok va yopish ikkalasi xato bersa —
SuppressedError(error+suppressed);await using— asinxron yopish. - TC39 4-bosqich (ES2027), Node 24 va Chrome/Firefox'da bor, Safari'da yo'q — Baseline emas: Node'da ishlating, brauzerda hozircha
try...finally.
Keyingi dars: Nega modullar va ESM export — vazifalar.js ni fayllarga bo'lishni boshlaymiz: nega bitta katta fayl muammo va export nima beradi.
Manbalar
- MDN: "using", "Symbol.dispose", "DisposableStack", "SuppressedError", "JavaScript resource management" — developer.mozilla.org
- TC39: "Explicit Resource Management" taklifi — github.com/tc39/proposal-explicit-resource-management; Finished Proposals (ES2027)
- web-features 3.40 (npm paketi):
explicit-resource-management— Baseline emas (Chrome 134, Firefox 141, Safari yo'q) - Node.js 24: "fs.mkdtempDisposableSync()", "Timers — timeoutSymbol.dispose" — nodejs.org/api
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!