Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Stack trace'ni o'qish
- 2.1 Yuqoridan pastga
- 2.2 Node'ning o'z qatorlari
- 2.3 Async stack trace
- 3. Stackni boshqarish
- 3.1 Error.captureStackTrace va stackTraceLimit
- 4. Source map: dist/ dan src/ ga
- 4.1 Muammo
- 4.2 Ikki yo'l
- 4.3 Kanon topilmasi: statik importlar oldin baholanadi
- 4.4 Biz takrorladik
- 4.5 Narxi va chegarasi
- 5. error.cause: sabablar zanjiri
- 5.1 Nega
- 5.2 Kanonda ko'rindi
- 5.3 AggregateError: bir nechta xato birdan
- 6. CLI qadami: holat 46 va commit
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): stackni o'qing
- 2-mashq (o'rta): wrap yordamchisi
- 3-mashq (qiyin): stack chiqishini tozalash
- 4-mashq: CLI qadami
- 9. Real ishda
- Xulosa
- Manbalar
Node.js stack trace, source map va error.cause: xato xabarini to'g'ri o'qish
Qisqacha: Stack trace — xato paydo bo'lgan joydan boshlab "kim kimni chaqirgan" ro'yxati. Yuqoridagi qator — xato joyi, pastdagilar — chaqiruvchilar.
awaitzanjiriat async …qatorlari bilan saqlanadi, eski callback'lar (setTimeout) esa zanjirni uzadi.tscbilan build qilingan kodda qatorlardist/*.jsni ko'rsatadi —.tsga qaytarish uchun source map kerak:--enable-source-mapsyokimodule.setSourceMapsSupport(true). Kanon topilmasi:setSourceMapsSupportni statik importlardan keyin chaqirsangiz, ular uchun ishlamaydi — kodni dinamikimport()bilan yuklash kerak.error.causexatoga asl sababni biriktiradi:CBU API: tarmoq xatosi ← fetch failed ← connect ECONNREFUSED.
Bu darsda
- Stack trace'ni yuqoridan pastga o'qib, xato qayerda va qanday yo'l bilan paydo bo'lganini topasiz.
- Async stack trace qachon saqlanishini va qachon uzilishini bilasiz.
Error.captureStackTrace,Error.stackTraceLimit,--stack-trace-limitbilan stackni boshqarasiz.- Source map'ni Node'da yoqasiz va ESM import tartibi tuzog'ini tushuntira olasiz.
error.causezanjirini vaAggregateErrorni ishlatasiz;valyutaning holat 46 ini commit qilasiz.
Oldin bilishingiz kerak: Stack trace va JavaScript xato turlari, throw va Error obyekti, O'z xato klasslaringiz, Source map'lar, Node'ni debug qilish.
1. Nega bu kerak?
Foydalanuvchi yozdi: "valyuta tahlil ishlamayapti". Siz NODE_DEBUG=valyuta bilan stackni so'radingiz va oldingiz:
Error: 1-qator "sana,kod,nomi,nominal,kurs,farq" bo'lishi kerak, topildi: "yomon,sarlavha"
at tahlilQil (file:///C:/Users/…/valyuta/dist/tahlil.js:42:23)dist/tahlil.js:42 — tsc yaratgan fayl. Siz esa src/tahlil.ts ni tahrirlaysiz, unda 42-qator boshqa joy. Xato xabari ham bir qatorli: nega? kim chaqirdi? CBU'ga ulanmagan bo'lsa — nega ulanmadi?
09-qismda brauzerdagi stack trace'ni o'qigansiz. Bu darsda Node'ga xos uch narsani qo'shamiz: async zanjir, build qilingan kod (source map) va xato sabablari zanjiri (cause). Shulardan keyin yuqoridagi xabar src\tahlil.ts:47:41 va src\cli.ts:66:18 ko'rinishida keladi.
O'xshatish: pochta posilkasi. Stack trace — posilka qaysi bo'limlardan o'tganini ko'rsatadigan muhrlar. Source map — "eski manzil → yangi manzil" jadvali (bo'lim ko'chgan bo'lsa ham to'g'ri joyni topasiz). cause — "qaytarildi, chunki: manzil topilmadi, chunki: uy raqami yo'q" zanjiri.
2. Stack trace'ni o'qish
2.1 Yuqoridan pastga
Uch funksiya bir-birini chaqiradi; eng ichkarisida xato. Blok Node uchun — o'z terminalingizda ishga tushiring. short yordamchisi uzun fayl yo'lini dars.mjs ga qisqartiradi (sizda fayl nomingiz chiqadi):
const short = (stack) =>
stack.replace(/file:\/\/\/\S+?\.mjs/g, "dars.mjs").split("\n");
function unitRate(k) {
if (!(k.nominal > 0)) {
throw new RangeError(`Nominal noto'g'ri: ${k.nominal}`);
}
return k.kurs / k.nominal;
}
function convert(amount, from) {
return amount * unitRate(from);
}
function main() {
return convert(100, { kod: "IDR", kurs: 6.59, nominal: 0 });
}
try {
main();
} catch (error) {
console.log(short(error.stack).slice(0, 5).join("\n"));
}Konsolda:
RangeError: Nominal noto'g'ri: 0
at unitRate (dars.mjs:6:11)
at convert (dars.mjs:11:19)
at main (dars.mjs:14:10)
at dars.mjs:18:3| Qator | Ma'nosi |
|---|---|
RangeError: … |
Xato turi va xabari |
at unitRate (…:6:11) |
Xato shu yerda tashlandi: 6-qator, 11-ustun (throw) |
at convert (…:11:19) |
unitRate ni chaqirgan joy |
at main (…:14:10) |
convert ni chaqirgan joy |
at dars.mjs:18:3 |
Modulning yuqori darajasi — main() |
Qoida: birinchi at qatori — xato joyi, qolganlari — u yerga qanday kelinganining yo'li. Ko'pincha xato sababi birinchi qatorda emas, pastroqda: bu yerda unitRate to'g'ri ishladi, noto'g'ri nominal: 0 ni main bergan.
2.2 Node'ning o'z qatorlari
Haqiqiy chiqishda pastda yana qatorlar bo'ladi — node:internal/…:
at ModuleJob.run (node:internal/modules/esm/module_job:561:25)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)Bu — Node'ning modul yuklovchisi. Sizning kodingiz emas, o'qishda o'tkazib yuboring. Faqat file:// yoki sizning papkangizdagi yo'l bilan boshlanganlariga qarang.
2.3 Async stack trace
await bilan chaqirilgan funksiyalar zanjiri saqlanadimi?
const short = (stack) =>
stack.replace(/file:\/\/\/\S+?\.mjs/g, "dars.mjs").split("\n");
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
async function fetchRate() {
await wait(10);
throw new Error("CBU javob bermadi");
}
async function showRate() {
return await fetchRate();
}
try {
await showRate();
} catch (error) {
console.log(short(error.stack).slice(0, 4).join("\n"));
}
function oldStyle(callback) {
setTimeout(() => callback(new Error("eski uslub")), 10);
}
oldStyle((error) => {
console.log(short(error.stack).slice(0, 4).join("\n"));
});Konsolda:
Error: CBU javob bermadi
at fetchRate (dars.mjs:7:9)
at async showRate (dars.mjs:10:10)
at async dars.mjs:14:3
Error: eski uslub
at Timeout._onTimeout (dars.mjs:20:29)
at listOnTimeout (node:internal/timers:685:17)
at process.processTimers (node:internal/timers:618:7)Birinchi xato await wait(10) dan keyin tashlandi — event loop bir aylanib qaytdi. Shunga qaramay zanjir to'liq: at async showRate, at async dars.mjs. V8 buni async stack trace deb ataydi: await lar zanjirini eslab qoladi.
Ikkinchisi — eski callback uslubi. Stack Timeout._onTimeout dan boshlanadi va oldStyle ni kim chaqirgani yo'q. Taymer ishga tushganda asl chaqiruvchi allaqachon tugagan. Bu — callback'lardan async/await ga o'tishning yana bir sababi.
Tekshirib ko'ring:
async function showRate() { return fetchRate(); }—awaitsiz. Stackdaat async showRateqatori bo'ladimi?
Javob
Yo'q bo'lishi mumkin. V8 async zanjirni await nuqtalari orqali tiklaydi. return fetchRate() da showRate Promise'ni darhol qaytaradi va zanjirdan chiqib ketadi — xato tashlanganda u "kutayotgan" funksiya emas. Shuning uchun ko'p qo'llanmalar return await ni try ichida va stack muhim joyda tavsiya qiladi. O'zingiz yuqoridagi blokda await ni olib tashlab tekshiring.
3. Stackni boshqarish
3.1 Error.captureStackTrace va stackTraceLimit
class UsageError extends Error {
constructor(message) {
super(message);
this.name = "UsageError";
Error.captureStackTrace(this, UsageError);
}
}
function parseAmount(text) {
const n = Number(text);
if (Number.isNaN(n)) {
throw new UsageError(`Miqdor son emas: ${text}`);
}
return n;
}
try {
parseAmount("yuz");
} catch (error) {
const lines = error.stack.split("\n");
console.log(lines[0]);
console.log(lines[1].trim().split(" ")[1]);
}
Error.stackTraceLimit = 1;
function a() { b(); }
function b() { throw new Error("chuqur"); }
try { a(); } catch (e) { console.log(e.stack.split("\n").length); }Konsolda:
UsageError: Miqdor son emas: yuz
parseAmount
2Error.captureStackTrace(obj, fn)—obj.stackni hozirgi joydan yozadi vafndan yuqoridagi qatorlarni (bu yerda konstruktorning o'zini) yashiradi. Natijada birinchiatqatori —parseAmount, ya'ni xato haqiqatan qayerda aniqlangan joy. Kutubxonalar o'z ichki funksiyalarini stackdan shunday olib tashlaydi.Error.stackTraceLimit = 1— nechtaatqatori yig'iladi (sukut — 10). Natija 2 qator: xabar + 1 qator.
Bayroq bilan ham — butun jarayon uchun. Biz ishga tushirdik (3 chuqurlikdagi xato):
node --stack-trace-limit=2 lim.mjsError: chuqur
at c (file:///C:/Users/…/lim.mjs:3:22)
at b (file:///C:/Users/…/lim.mjs:2:16)Bayroqsiz shu faylda 7 ta at qatori chiqdi (Node ichkilari bilan). Chuqur rekursiya yoki ko'p qatlamli framework'da 10 kam bo'lishi mumkin — --stack-trace-limit=50. Hujjatga ko'ra 0 — stack umuman yig'ilmaydi.
4. Source map: dist/ dan src/ ga
4.1 Muammo
16-qismda source map'ni brauzer uchun ko'rgansiz: .map fayli "yig'ilgan koddagi qator → manbadagi qator" jadvali. valyuta ning tsconfig.build.json ida "sourceMap": true bor — dist/tahlil.js yonida dist/tahlil.js.map:
{"version":3,"file":"tahlil.js","sourceRoot":"","sources":["../src/tahlil.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU…Bu — biz yasagan kichik misolning .map fayli boshi. sources — manba fayl, mappings — qator-ustun moslik jadvali (VLQ kodlashda). Brauzer DevTools uni o'zi o'qiydi. Node esa sukut bo'yicha o'qimaydi.
4.2 Ikki yo'l
| Yo'l | Qachon |
|---|---|
node --enable-source-maps dist/bin.js |
Bayroq — ishga tushirgan odam beradi |
module.setSourceMapsSupport(true) |
Kod ichidan — CLI foydalanuvchisi bayroq bermaydi |
valyuta — npm'dan o'rnatiladigan CLI. Foydalanuvchi npx valyuta yozadi, bayroq yozmaydi. Shuning uchun kanon ikkinchi yo'lni tanladi.
4.3 Kanon topilmasi: statik importlar oldin baholanadi
Kanon birinchi variantda bin.ts boshiga yozdi:
import { setSourceMapsSupport } from "node:module";
import { main } from "./cli.ts";
setSourceMapsSupport(true);
process.exitCode = await main(process.argv.slice(2), process.env);Mantiq oddiy tuyuladi: avval yoqamiz, keyin ishlaymiz. Natija:
VALYUTA 10036: Error: 1-qator "sana,kod,nomi,nominal,kurs,farq" bo'lishi kerak, topildi: "yomon,sarlavha"
at tahlilQil (file:///C:/Users/…/valyuta/dist/tahlil.js:42:23)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async tahlilBuyrugi (file:///C:/Users/…/valyuta/dist/cli.js:62:20)
at async main (file:///C:/Users/…/valyuta/dist/cli.js:126:20)Hamon dist/*.js. Sabab — ES Modules darsidagi qoida: statik import lar modul kodi ishlashidan oldin yuklanadi va baholanadi. cli.ts, tahlil.ts va boshqalar setSourceMapsSupport(true) qatoriga yetmasdan oldin yuklangan. Source map esa modul yuklanayotganda o'qiladi — kech qoldi.
Node 24 hujjati ham buni tasdiqlaydi: source map faqat yoqilgandan keyin yuklangan fayllar uchun o'qiladi. Shuning uchun hujjat iloji bo'lsa --enable-source-maps bayrog'ini tavsiya qiladi.
sequenceDiagram
participant N as Node
participant B as bin.js
participant C as cli.js, tahlil.js
N->>C: 1. statik import'lar yuklanadi
N->>B: 2. bin.js kodi ishlaydi
B->>B: setSourceMapsSupport(true)
Note over C: map o'qilmadi —<br/>allaqachon yuklangan
B->>C: 3. await import() — yangi modul
Note over C: endi map o'qiladiYuqoridagi strelkalar — Node'ning haqiqiy tartibi: kodingiz yozilgan tartib emas. Tuzatish (kanon, holat 46):
import { setSourceMapsSupport } from "node:module";
// Source map modul YUKLANAYOTGANDA o'qiladi — shuning uchun
// cli.ts statik import emas, yoqilgandan KEYIN dinamik import
setSourceMapsSupport(true);
const { main } = await import("./cli.ts");Endi:
VALYUTA 9084: Error: 1-qator "sana,kod,nomi,nominal,kurs,farq" bo'lishi kerak, topildi: "yomon,sarlavha"
at tahlilQil (C:\Users\…\valyuta\src\tahlil.ts:47:41)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async tahlilBuyrugi (C:\Users\…\valyuta\src\cli.ts:66:18)
at async main (C:\Users\…\valyuta\src\cli.ts:129:39)src\tahlil.ts:47:41 — manbadagi aniq joy. tsc ning rewriteRelativeImportExtensions dinamik import("./cli.ts") ni ham ./cli.js ga almashtirdi — build'da hech narsa buzilmadi. VALYUTA 10036 va 9084 — debuglog prefiksi va jarayon raqami (PID).
4.4 Biz takrorladik
Kanon topilmasini kichik misolda qaytadan sinadik: src/tahlil.ts (xato tashlaydi) va ikki xil bin, tsc --sourceMap bilan build:
== statik import + setSourceMapsSupport
at qatorniOqi (file:///C:/Users/…/t46/dist/tahlil.js:4:15)
== dinamik import + setSourceMapsSupport
at qatorniOqi (C:\Users\…\t46\src\tahlil.ts:9:11)
== statik import + node --enable-source-maps
at qatorniOqi (C:\Users\…\t46\src\tahlil.ts:9:11)Uchinchi qator muhim: bayroq bilan statik import ham ishladi — --enable-source-maps Node ishga tushishidanoq yoqiladi, hech qanday modul undan oldin yuklanmaydi. Muammo faqat kod ichidan yoqishda.
4.5 Narxi va chegarasi
- Vaqt: kanonda
valyuta --helpsource map bilan 92.2 ms, usiz (main()to'g'ridan) 87.7 ms — +4.5 ms (15 marta, mediana). Hujjat ham ogohlantiradi:Error.stacko'qilganda kechikish bo'lishi mumkin. src/yo'q: npm paketigasrc/kirmaydi (files: ["dist", "fixtures"]). Qator raqamlari to'g'ri, lekinsrc\tahlil.tsfaylining o'zi foydalanuvchi kompyuterida yo'q.inlineSources: truebilan manba.mapichiga yoziladi — paket kattalashadi. Kanonda bu sozlama yo'q.node_modulesichida: npm bilan o'rnatilgan paketnode_modulespapkasida yashaydi. Hujjatga ko'rasetSourceMapsSupport(true)u yerdagi fayllarni sukut bo'yicha o'tkazib yuboradi (nodeModulessozlamasifalse). Biz kichik paket bilan sinadik (Node 24.21.0, Windows 11):setSourceMapsSupport(true)— stacknode_modules\…\dist\lib.jsni ko'rsatdi;{ nodeModules: true }bilan —…\src\lib.ts;--enable-source-mapsbayrog'i bilan ham.ts. Kanonbin.tsda bu sozlama yo'q. Demak o'rnatilganvalyutada stackdist/ni ko'rsatadi — kanon buni o'rnatilgan holatda sinamagan. Amalda farq kichik:src/baribir paketda yo'q..tsto'g'ridan:node src/bin.tsda source map umuman kerak emas — type stripping qator raqamlarini saqlaydi (biz ham tekshirdik:src/tahlil.ts:9:11).
5. error.cause: sabablar zanjiri
5.1 Nega
fetch darsida ko'rdingiz: tarmoq xatosi doim TypeError: fetch failed, asl sabab cause da. ES2022 dan har Error ga ikkinchi argument beriladi: new Error(xabar, { cause }). Xatoni ushlab, o'z tilingizda qayta tashlaysiz — lekin asl sababni yo'qotmaysiz.
import { inspect } from "node:util";
const network = new Error("connect ECONNREFUSED 127.0.0.1:59999");
const fetchError = new TypeError("fetch failed", { cause: network });
const apiError = new Error("CBU API: tarmoq xatosi", {
cause: fetchError,
});
function causeChain(error, depth = 5) {
const parts = [];
for (let e = error; e !== undefined && parts.length < depth; ) {
parts.push(e instanceof Error ? e.message : inspect(e));
e = e instanceof Error ? e.cause : undefined;
}
return parts.join(" ← ");
}
console.log(causeChain(apiError));
console.log(causeChain(new Error("x", { cause: { kod: 1 } })));
console.log(apiError.cause === fetchError, "cause" in new Error("y"));Konsolda:
CBU API: tarmoq xatosi ← fetch failed ← connect ECONNREFUSED 127.0.0.1:59999
x ← { kod: 1 }
true falsecause— istalgan qiymat bo'lishi mumkin (ikkinchi qatorda — oddiy obyekt). Shuning uchuninspectbilan matnga aylantiramiz.depth— cheksiz zanjirdan himoya (a.cause = akabi)."cause" in new Error("y")—false:causeberilmasa, xususiyat umuman yo'q.
Bu — valyuta ning sababZanjiri funksiyasining o'zi (src/xato.ts, kanon).
5.2 Kanonda ko'rindi
valyuta ning ogohlantirishlari endi asl sababni aytadi (kanon 46-xato.txt):
Ogohlantirish: API ishlamadi (CBU API: tarmoq xatosi ← fetch failed ← connect ECONNREFUSED 127.0.0.1:59999) — saqlangan namuna kurslar (fixtures/) ishlatildi
Ogohlantirish: API ishlamadi (CBU API: tarmoq xatosi ← fetch failed ← bad port) — saqlangan namuna kurslar (fixtures/) ishlatildi
Ogohlantirish: API ishlamadi (CBU API: tarmoq xatosi ← fetch failed ← getaddrinfo ENOTFOUND yoq.invalid) — saqlangan namuna kurslar (fixtures/) ishlatildiHolat 24 da hammasi bir xil edi: "CBU API: tarmoq xatosi". Endi uch xil holat uch xil ko'rinadi. 9-port fetch uchun "bloklangan port" ekanini net, dns va tls darsida ko'rgan edik — endi error.cause zanjiri buni kodning o'zida, foydalanuvchi xabarida ko'rsatadi.
console.log(xato) yoki ushlanmagan xatoda Node cause ni o'zi ham chiqaradi — [cause]: … bloki bilan (fetch darsidagi invalid onRequestStart method chiqishini eslang).
5.3 AggregateError: bir nechta xato birdan
Ba'zan sabab bitta emas. Promise.any — "birinchi muvaffaqiyatli" ni qaytaradi; hammasi yiqilsa — AggregateError:
const mirrors = [
Promise.reject(new Error("cbu.uz: timeout")),
Promise.reject(new Error("zaxira: 503")),
];
try {
await Promise.any(mirrors);
} catch (error) {
console.log(error.name, "|", error.message);
for (const e of error.errors) console.log(" -", e.message);
}
const both = [new Error("a"), new Error("b")];
const agg = new AggregateError(both, "2 ta xato");
console.log(agg.errors.length, agg instanceof Error);Konsolda:
AggregateError | All promises were rejected
- cbu.uz: timeout
- zaxira: 503
2 true"Hamma Promise'lar rad etildi". Har biri errors massivida. Bir nechta faylni tekshiradigan validator yoki bir nechta zaxira API'si — AggregateError ning tabiiy joyi.
Tekshirib ko'ring:
catch (e) { throw new Error("Kesh o'qilmadi"); }— bu qatorning muammosi nima va qanday tuzatasiz?
Javob
Asl xato (EACCES? SyntaxError? ENOENT?) yo'qoladi — log'da faqat "Kesh o'qilmadi" qoladi va sababini hech kim bilmaydi. Tuzatish: throw new Error("Kesh o'qilmadi", { cause: e }); — stack va xabar saqlanadi, sababZanjiri uni to'liq ko'rsatadi.
6. CLI qadami: holat 46 va commit
Holat 46 uch darsning yakuni: fetch (User-Agent, signal), debug (NODE_DEBUG da to'liq stack) va bugungi qism. Bugun qo'shilganlar:
| Fayl | O'zgarish |
|---|---|
src/xato.ts |
Yangi: sababZanjiri (≤ 5), bekorQilinganmi (AbortError) |
src/bin.ts |
setSourceMapsSupport(true) + dinamik import("./cli.ts"); Ctrl+C → AbortController |
src/cli.ts |
Xato: sababZanjiri; debug("%s", xato.stack); AbortError → exit 130 |
src/manba.ts |
Ogohlantirishlarda sababZanjiri |
| testlar | +2: zanjir va User-Agent; bekor qilingan signal → 130 |
bin.ts dagi Ctrl+C mantiqi: birinchi Ctrl+C — bekor.abort() (kanon kodiga ko'ra so'rov, kutish, oqim to'xtaydi, vaqtinchalik fayllar tozalanadi; haqiqiy terminalda Ctrl+C sinalmagan), ikkinchisi — darhol process.exit(130). Halol eslatma: haqiqiy Ctrl+C sinalmagan — Windows'da boshqa jarayondan yuborilgan SIGINT ishlovchini chaqirmaydi (process darsi), agentda esa konsol yo'q. Test buni modellaydi: main() ga 100 ms da bekor qilinadigan signal beriladi, 3 s kechikadigan server bilan — natija 130.
Yana bir halol eslatma: ikkinchi Ctrl+C dagi process.exit(130) hech narsani kutmaydi. Agar u tarix faylga yozayotgan paytga to'g'ri kelsa, vaqtinchalik .tmp fayl diskda qolishi mumkin (Xavfsiz fayl operatsiyalari darsidagi temp + rename). Asosiy fayl buzilmaydi, faqat chiqindi qoladi. Kanon bu holatni sinamagan.
Tekshiring:
npm run check
npm run build
NODE_DEBUG=valyuta node dist/bin.js tahlil yomon.csvOxirgisida stack src\…\tahlil.ts ni ko'rsatishi kerak. Kanon natijasi (holat 46): build 1.9 s, tip 0, lint 0, testlar 41/41; pack 56 fayl, 27.4 kB; sovuq start 93/96 ms.
Commit (kanon):
git add -A
git commit -m "Ctrl+C so'rov va oqimni bekor qiladi, xato sababi zanjiri, source map, User-Agent"7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Stack dist/*.js ni ko'rsatadi |
Source map yoqilmagan | --enable-source-maps yoki setSourceMapsSupport |
setSourceMapsSupport bor, baribir dist |
Statik importlar oldin yuklangan | Keyin dinamik import() |
| Stack callback'dan boshlanadi, chaqiruvchi yo'q | setTimeout/callback zanjirni uzadi |
async/await |
at async … yo'q |
return await siz |
return await |
| Stack 10 qatorda uzildi | Error.stackTraceLimit sukut 10 |
--stack-trace-limit=50 |
Log'da faqat fetch failed |
cause chiqarilmagan |
sababZanjiri / util.inspect |
| Qayta tashlangan xatoda asl sabab yo'q | { cause } berilmagan |
new Error(x, { cause: e }) |
Stack chalkash: MyError konstruktori birinchi |
Konstruktor stackda | Error.captureStackTrace(this, MyError) |
8. Mashqlar
Yechimlar kurs/mashqlar/24/46-stack-cause/ da.
1-mashq (oson): stackni o'qing
TypeError: Cannot read properties of undefined (reading 'kurs')
at birlikKursi (file:///…/src/kurs.ts:49:12)
at konvert (file:///…/src/kurs.ts:53:22)
at bajar (file:///…/src/buyruq.ts:31:18)
at main (file:///…/src/cli.ts:120:11)- Xato tashlangan fayl va qator —
kurs.ts: birlikKursini chaqirgan funksiya —- Eng tashqi (birinchi chaqirilgan) funksiya —
birlikKursiga nima keldi?
Yechim
- 49 — birinchi
atqatori. - konvert — ikkinchi qator.
- main — oxirgi qator.
- undefined —
(reading 'kurs'):undefined.kurs. Sababini pastroqda —konvertga noto'g'ri valyuta (topilmagan kod) berganbajarda qidiring.
2-mashq (o'rta): wrap yordamchisi
wrap(message, fn) funksiyasini yozing: fn() ni chaqiradi, xato bo'lsa new Error(message, { cause }) bilan qayta tashlaydi; fn async bo'lsa ham ishlasin. Ikki qatlamli zanjir quring va causeChain (darsdagidek) bilan chiqaring.
Yechim
async function wrap(message, fn) {
try {
return await fn();
} catch (cause) {
throw new Error(message, { cause });
}
}
function causeChain(error) {
const parts = [];
let e = error;
while (e instanceof Error && parts.length < 5) {
parts.push(e.message);
e = e.cause;
}
return parts.join(" ← ");
}
const readCache = () => JSON.parse("{buzilgan");
try {
await wrap("Kurs olinmadi", () =>
wrap("Kesh o'qilmadi", async () => readCache()));
} catch (error) {
console.log(causeChain(error));
}
console.log(await wrap("kerak emas", async () => 11903.15));Konsolda:
Kurs olinmadi ← Kesh o'qilmadi ← Expected property name or '}' in JSON at position 1 (line 1 column 2)
11903.15return await fn() — await siz try/catch async xatoni ushlamasdi: Promise try blokidan chiqib ketgan bo'lardi. Muvaffaqiyatli holatda wrap qiymatni o'zgarishsiz qaytaradi.
3-mashq (qiyin): stack chiqishini tozalash
Stack matnini foydalanuvchiga ko'rsatish uchun tozalaydigan cleanStack(stack, root) yozing. U uch ish qilsin: (1) node:internal qatorlarini olib tashlaydi; (2) root papka yo'lini . ga almashtiradi (Windows va URL ko'rinishidagi ikkala yo'l uchun); (3) bir xil qatorlar ketma-ket takrorlansa (rekursiya), ularni … (N marta takrorlandi) bilan qisqartiradi.
Yechim
function cleanStack(stack, root) {
const rootUrl = "file:///" + root.replaceAll("\\", "/");
const lines = stack
.split("\n")
.filter((l) => !l.includes("node:internal"))
.map((l) => l.replaceAll(rootUrl, ".").replaceAll(root, "."));
const out = [];
for (let i = 0; i < lines.length; ) {
let j = i;
while (j + 1 < lines.length && lines[j + 1] === lines[i]) j++;
out.push(lines[i]);
if (j > i) out.push(` … (${j - i} marta takrorlandi)`);
i = j + 1;
}
return out.join("\n");
}
const root = "C:\\loyiha\\valyuta";
const stack = [
"RangeError: Maximum call stack size exceeded",
" at kunlar (file:///C:/loyiha/valyuta/src/tarix.ts:12:3)",
" at kunlar (file:///C:/loyiha/valyuta/src/tarix.ts:12:3)",
" at kunlar (file:///C:/loyiha/valyuta/src/tarix.ts:12:3)",
" at main (C:\\loyiha\\valyuta\\src\\cli.ts:120:11)",
" at ModuleJob.run (node:internal/modules/esm/module_job:561)",
].join("\n");
console.log(cleanStack(stack, root));Konsolda:
RangeError: Maximum call stack size exceeded
at kunlar (./src/tarix.ts:12:3)
… (2 marta takrorlandi)
at main (.\src\cli.ts:120:11)Source map yoqilgan Windows yo'li (C:\…) va yoqilmagan URL (file:///C:/…) — bitta stackda ikkalasi uchrashi mumkin (kanondagi "oldin/keyin" chiqishlarini solishtiring), shuning uchun ikkalasi ham almashtiriladi.
4-mashq: CLI qadami
src/xato.ts ni yarating (sababZanjiri, bekorQilinganmi), manba.ts va cli.ts dagi xabarlarni zanjirga o'tkazing. bin.ts da setSourceMapsSupport(true) va dinamik import("./cli.ts"). Tekshiring: (1) avval statik import bilan npm run build + NODE_DEBUG=valyuta node dist/bin.js tahlil yomon.csv — stack dist/ ni ko'rsatadi; (2) dinamik import bilan — src\…\.ts. npm run check toza bo'lsa, holat 46 ni commit qiling.
Yechim
Kod — «CLI qadami» jadvali va «Source map» bo'limidagi kanon bin.ts. yomon.csv — birinchi qatori yomon,sarlavha bo'lgan istalgan fayl. (1) va (2) natijalari — kanon chiqishlari (dist/tahlil.js:42:23 → src\tahlil.ts:47:41). Commit xabari — «CLI qadami» bo'limidagi kanon xabari.
9. Real ishda
- Xato kuzatish xizmatlari (Sentry va boshqalar — 21-qismda frontend uchun) serverda ham source map bilan ishlaydi: prod'da
dist, hisobotda —srcqatorlari. - Log'lar —
causezanjirini JSON log'ga yozish (err.causeni ham serializatsiya qilish) — tunda yiqilgan serverni ertalab tushunishning yagona yo'li. - Kutubxona mualliflari
Error.captureStackTraceva o'z xato klasslari bilan foydalanuvchiga keraksiz ichki qatorlarni ko'rsatmaydi. - Intervyu savollari: "Stack trace'ni qanday o'qiysiz?", "
error.causenima uchun?", "Node'da TypeScript xato qatorlarini qanday to'g'ri ko'rsatasiz?".
Xulosa
- Stack: birinchi
at— xato joyi, qolgani — u yerga yo'l;node:internalqatorlarini o'tkazib yuboring. awaitzanjiriat async …bilan saqlanadi;setTimeout/callback zanjirni uzadi;return awaitmuhim.Error.captureStackTrace(this, Klass)— keraksiz qatorlarni yashiradi;stackTraceLimitsukut 10,--stack-trace-limitbilan o'zgaradi.- Source map:
--enable-source-mapsyokisetSourceMapsSupport(true)— ikkinchisi statik importlardan keyin ishlamaydi, kodni dinamikimport()bilan yuklang (kanon topilmasi, +4.5 ms). new Error(x, { cause })— asl sababni saqlang; zanjirni chiqaring (A ← B ← C); bir nechta sabab —AggregateError.
Keyingi dars: CPU profiling va flamegraph — sekin kodni taxmin bilan emas, --cpu-prof profili bilan topish: valyuta tahlil vaqtining 22 % i qayerga ketadi.
Manbalar
- Node.js 24 hujjati — Errors (
error.stack,Error.captureStackTrace,Error.stackTraceLimit,cause): https://nodejs.org/docs/latest-v24.x/api/errors.html - Node.js 24 hujjati — Modules:
module.setSourceMapsSupport; CLI:--enable-source-maps,--stack-trace-limit: https://nodejs.org/docs/latest-v24.x/api/module.html , https://nodejs.org/docs/latest-v24.x/api/cli.html - V8 — Stack trace API (
Error.captureStackTrace,stackTraceLimit): https://v8.dev/docs/stack-trace-api - MDN —
Error.cause,AggregateError,Promise.any: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause - Kanon:
valyutaholat 46 (xato.ts,bin.ts),natijalar/46-xato.txt(statik/dinamik import stacklari,causezanjirlari),t45-debug(source map narxi). Kichik misollar (--stack-trace-limit, statik/dinamik/bayroq taqqoslash,node_modulesvanodeModulessozlamasi) — biz ishga tushirdik (Node 24.21.0, typescript 6.0.3, Windows 11).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!