App ou se pare. Ou gen yon backend ki fè kèk bagay magik ak Lè sa a ekspoze kèk done nan yon API. Ou gen yon frontend ki konsome API sa a ak montre done a itilizatè a. Ou itilize Fetch API a fè demann nan backend ou a, Lè sa a, pwosesis repons la ak mete ajou UI a. Senp ak senp, dwa? Bon, nan devlopman, si. Lè sa a, ou deplwaye app ou nan pwodiksyon. Apre sa, bagay enteresan kòmanse rive. Pifò nan tan, tout sanble ok, men pafwa, demann yo manke. UI a kòrèk. itilizatè yo ankouraje. Ou kesyon sa ki te ale mal. Rezo a se imprevizib, epi ou gen yo dwe pare pou li. Ou ta dwe pi bon gen repons yo nan kesyon sa yo: Ki sa ki rive lè rezo a se lontan oswa enpòtan? Ki sa ki rive lè backend la se ba oswa retounen yon erè? Si ou konsome API ekstèn, ki sa ki rive lè ou rive limite vitès la ak jwenn bloke? Ki jan ou ap travay ak senaryo sa yo gracefully ak bay yon eksperyans itilizatè bon? Onètman, Vanilla Fetch pa ase pou fè fas a nan senaryo sa yo. Ou bezwen ajoute yon anpil nan kòd boilerplate yo fè fas a, retries, timeouts, caching, elatriye Sa a ka byen vit vin chaje ak difisil yo kenbe. Nan atik sa a, nou pral eksplore ki jan yo fè ou retrè demann pwodiksyon-prepare lè l sèvi avèk yon livrezon rele Nou pral: ffetch bati yon backend ak Node.js ak Express ak kèk Endpoints bati yon frontend ki sonde sa yo endpoints ak vanilla JavaScript lè l sèvi avèk Fetch API fè backend la flaky pou simile senaryo reyèl wè ki jan retire demann ka rezoud ak ki jan yo rezoud rezoud yo entwodui ffetch pou senplize ak amelyore manyen demann jwenn Pwodwi pou Nou pral bati eskalye yo nan yon lis travay senp plizyè itilizatè. Backend la pral ekspoze RESTful endpoints yo kreye, li, mete ajou, ak retire itilizatè yo ak travay, ak alokasyon travay nan itilizatè yo. Frontend la pral sonde backend a pou jwenn done yo dènye ak montre li a itilizatè a. Retounen Nou pral sèvi ak Node.js ak Express yo bati backend la. Nou pral tou sèvi ak yon magazen done senp nan memwa kenbe bagay la senp. Isit la yo se itilizatè a ak modèl travay: interface User { id: number; // Unique identifier name: string; // Full name email: string; // Email address } export interface Task { id: number; // Unique identifier title: string; // Short task title description?: string; // Optional detailed description priority: "low" | "medium" | "high"; // Task priority } Nou pral kreye pwen fin vye granmoun yo: GET /users: Jwenn tout itilizatè (retire yon seri nan id itilizatè) POST /users: Kreye yon itilizatè nouvo (retire id nan itilizatè a kreye) GET /users/:id: Jwenn yon itilizatè pa id (retire objè a itilizatè) PUT /users/:id: Update yon itilizatè (retire yon mesaj siksè) DELETE /users/:id: Retire yon itilizatè (retire yon mesaj siksè) GET /tasks: Jwenn tout aktivite (retire yon seri id aktivite) GET /tasks/:id: Jwenn yon travay pa id (retire objè a nan travay) POST /tasks: Kreye yon nouvo aktivite (retire id nan aktivite a kreye) PUT /tasks/:id: Update yon travay (retire yon mesaj siksè) DELETE /tasks/:id: Retire yon travay GET /users/:userId/tasks: Jwenn tout travay yo bay yon itilizatè (retire yon seri id travay) POST /users/:userId/tasks/:taskId: Kite yon travay nan yon itilizatè (retire yon mesaj siksè) DELETE /users/:userId/tasks/:taskId: Retire yon travay nan yon itilizatè (retire yon mesaj siksè) Frontèn Kòm frameworks anjeneral ajoute abstraksyon yo pwòp yo ak fason yo fè bagay yo, nou pral sèvi ak vanilla TypeScript kenbe bagay yo senp ak anjeneral-framework. Nou pral kreye yon SPA ak de gade: youn pou lis la itilizatè ak youn pou yon itilizatè espesifik. Lis la itilizatè montre non itilizatè a ak nimewo a nan aktivite yo bay yo. Klike sou yon itilizatè pral pran ou nan View la itilizatè, ki montre detay yo nan itilizatè a ak travay yo. Apre sa, soti nan View la itilizatè, nou ka ale tounen nan lis la itilizatè. Pou kenbe bagay la senp, nou pral sèvi ak sondaj pou jwenn done yo dènye soti nan backend la. Chak 3 segonn, nou pral fè demann nan backend a pou jwenn done yo dènye pou vize a kounye a ak mete ajou UI a dapre. Pou View lis itilizatè, nou pral fè yon demann nan pou jwenn tout id itilizatè, Lè sa a, pou chak itilizatè, nou pral fè yon demann pou retabli detay yo, ak Kalkile nimewo a nan travay yo bay yo. GET /users GET /users/:id GET /users/:id/tasks Pou gade nan itilizatè a, nou pral fè yon demann nan pou jwenn detay nan itilizatè a, ak pou yo jwenn id nan travay yo bay yo. Lè sa a, pou chak id nan travay, nou pral fè yon demann pou Pou retabli detay nan travay la. GET /users/:id GET /users/:id/tasks GET /tasks/:id Repo nan GitHub Ou ka jwenn kòd la konplè pou egzanp sa a nan ladan . GitHub Repò Akòz kantite boilerplate, referans repo a pou kòd la konplè. Chak etap nan atik la pral referans yon branch nan repo a. Repò a gen tou de backend ak frontend kòd. Backend la se nan folder, ak frontend la se nan folder. Lè ou klone repò a, kouri nan tou de dosye yo enstale depannasyon yo. Lè sa a, ou ka kouri backend la ak nan folder, ak frontend la ak nan folder. Frontend la pral serve nan Kòm yon backend nan . backend frontend npm install npm run dev backend npm run dev frontend http://localhost:5173 http://localhost:3000 Yon fwa ou te fè tout travay yo ak tou de backend ou ak frontend yo kouri, ou ka louvri navigatè ou ak ale nan Pou wè app la nan aksyon: http://localhost:5173 nan devlopman Si ou navige nan , ou ta dwe wè tout bagay travay jis ok. Si ou ajoute yon itilizatè nouvo ak http://localhost:5173 curl -X POST http://localhost:3000/users \ -H "Content-Type: application/json" \ -d '{"name": "John Doe", "email": "john@example.com"}' ou ta dwe wè itilizatè a ap parèt nan View lis itilizatè nan 3 segonn. Senpleman santi yo lib yo jwe ak app a ak ajoute plis itilizatè ak travay. Pifò chans, tout pral travay jis ok. Bon, sa a se kote nou finalman rive nan pwen an nan atik sa a. Backend nou an travay jis byen. Frontend nou an, malgre boilerplate la eksepsyonèl, tou travay jis byen. Men, ant frontend la ak backend, gen rezo a. Ak rezo a se pa konfyans. Se konsa, wè sa ki ka pase si nou ajoute yon ti kras flakiness nan backend nou an. Similasyon erè rezo Tanpri ajoute yon middleware nan backend nou an ki ale atansyon rezo ak yon chans 20% ak tou ajoute kèk retard atansyon jiska 1 segonn. Sa a pral simile erè rezo ak ede nou tès ki jan frontend nou an trete yo. Ou ka jwenn midwifè a flaky nan Kòd la se isit la: backend/src/middleware/flaky.ts import { Request, Response, NextFunction } from 'express'; export function flaky(req: Request, res: Response, next: NextFunction) { // Randomly fail requests with a 20% chance if (Math.random() < 0.2) { return res.status(500).json({ error: 'Random failure' }); } // Add random delay up to 2 seconds const delay = Math.random() * 2000; setTimeout(next, delay); } Lè sa a, nou ka sèvi ak middleware sa a nan Express app nou an. Ou ka jwenn kòd la nan jis enpòte middleware a ak sèvi ak li anvan wout ou: backend/src/index.ts ... import { flaky } from './middleware/flaky'; ... app.use(cors()); app.use(express.json()); app.use(flaky); // Use the flaky middleware Kòd sa a se nan branch nan repo a, se konsa ou ka tcheke li ak . network-errors git checkout network-errors Koulye a, si ou reboot backend ou ak refresh frontend la, ou ta dwe kòmanse wè kèk bagay enteresan. Konsole a pral ranpli ak erè. Gen kèk jaden sou UI a pral Men, sa a se lè, si ou pa deja gen, ou bezwen kòmanse panse sou ki jan yo travay ak erè sa yo gracefully. undefined Scenè erè Premyèman, nou pral identifye sa ki ka ale mal ak ki jan nou ka travay avèk li: Rechèch rezo intermittent: Rechèch ka rezoud aleksyonèlman, se konsa nan kèk erè, nou bezwen rechèch yo kèk fwa anvan ou kite. Nan sondaj, nou pa voye sèlman yon sèl demann, men plizyè demann asynchronously. Apre 3 segonn, nou voye yon lòt batch de demann. Si yon demann soti nan batch anvan yo toujou ap tann lè batch la pwochèn se voye, nou ka jwenn yon repons pi bonè apre yon pita. Sa a ka mennen nan yon estati UI inkonsistent. Nou bezwen asire w ke sèlman repons la dènye se itilize pou mete ajou UI a, se konsa, lè yon nouvo sik sondaj kòmanse, nou bezwen anile nenpòt demann ki ap tann soti nan sik la anvan yo. Epitou, si itilizatè a navige nan yon View diferan pandan y ap demand soti nan View anvan yo toujou peze, nou ka jwenn repons pou View anvan nou te deja navige soti. Sa a ka tou mennen nan yon estati UI inkonsistent. Nou bezwen asire w ke sèlman repons yo pou View a kounye a yo itilize pou mete ajou UI a, se konsa lè navige nan yon View diferan, nou bezwen anile nenpòt kesyon peze soti nan View anvan. Si yon demann te siksè nan yon pwen, men Lè sa a, échoue nan yon sik sondaj pita, nou pa vle imedyatman montre yon estati erè nan itilizatè a. Nou ka cache repons yo siksè pou itilizatè yo pa wè chak ti kras nan rezo a. Nou bezwen fè fas ak erè 404 graceously ak navige tounen nan View lis itilizatè, oswa omwen montre yon mesaj pa te jwenn. Epitou, nou bezwen fè fas a senaryo kote backend la se konplètman ba oswa pa ka rive. Nou bezwen montre yon mesaj erè mondyal nan itilizatè a ak ka retounen demann yo apre yon tan. Epi lis la kontinye, espesyalman si UI a pèmèt kreye, mete ajou, oswa retire done yo. Men, pou kounye a, nou konsantre sou operasyon yo li ak ki jan yo trete erè lè retire done yo. Kòmanse erè ak Vanilla Fetch Isit la, tankou ak anpil bagay nan JavaScript (oswa TypeScript), ou gen de opsyon pou travay ak senaryo sa yo. Ou ka ekri fonksyon sèvis piblik ou pwòp ou yo ranpli API a Fetch ak ajoute logik la manyen erè nesesè, oswa ou ka chwazi yon livrezon ki fè sa pou ou. Kòmanse pa aplike tout nan tèt nou. Kòd la se sou branch nan repo a, se konsa ou ka tcheke li ak . native-fetch git checkout native-fetch Ki sa ki nesesè fè Centralize tout fèt logik nan poller.ts. Pou chak sondaj, kreye yon nouvo AbortController, ak anile anvan yo. Wrap jwenn apèl nan yon retry-and-timeout fonksyon. Sou siksè, mete ajou yon cache ak sèvi ak li pou rendu. Sou rezoud, rezoud si nesesè, ak sove tan / anilasyon graciously. nan nou Dosye a kounye a sanble tankou sa a: poller.ts // Cache for responses const cache: Record<string, any> = {}; // AbortController for cancelling requests let currentController: AbortController | undefined; // Helper: fetch with retries and timeout async function fetchWithRetry(url: string, options: RequestInit = {}, retries = 2, timeout = 3000): Promise<any> { for (let attempt = 0; attempt <= retries; attempt++) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeout); try { const res = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timer); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); return data; } catch (err) { clearTimeout(timer); if (attempt === retries) throw err; } } } // Cancel all previous requests function cancelRequests() { if (currentController) currentController.abort(); currentController = new AbortController(); } export async function fetchUserListData() { cancelRequests(); // Use cache if available if (cache.userList) return cache.userList; try { if (!currentController) throw new Error('AbortController not initialized'); const userIds = await fetchWithRetry('http://localhost:3000/users', { signal: currentController!.signal }); const users = await Promise.all(userIds.map((id: number) => fetchWithRetry(`http://localhost:3000/users/${id}`, { signal: currentController!.signal }))); const taskCounts = await Promise.all(userIds.map((id: number) => fetchWithRetry(`http://localhost:3000/users/${id}/tasks`, { signal: currentController!.signal }).then((tasks: any[]) => tasks.length))); cache.userList = { users, taskCounts }; return cache.userList; } catch (err) { // fallback to cache if available if (cache.userList) return cache.userList; throw err; } } export async function fetchUserDetailsData(userId: number) { cancelRequests(); const cacheKey = `userDetails_${userId}`; if (cache[cacheKey]) return cache[cacheKey]; try { if (!currentController) throw new Error('AbortController not initialized'); const user = await fetchWithRetry(`http://localhost:3000/users/${userId}`, { signal: currentController!.signal }); const taskIds = await fetchWithRetry(`http://localhost:3000/users/${userId}/tasks`, { signal: currentController!.signal }); const tasks = await Promise.all(taskIds.map((id: number) => fetchWithRetry(`http://localhost:3000/tasks/${id}`, { signal: currentController!.signal }))); cache[cacheKey] = { user, tasks }; return cache[cacheKey]; } catch (err) { if (cache[cacheKey]) return cache[cacheKey]; throw err; } } Nou te retire Kòm tout logik fetch se kounye a nan Nan ka itilize senp nou an, li se patnè, men menm isit la, nou te bezwen ajoute yon anpil nan kòd boilerplate pou fè fas, retries, timeouts, annulations, ak caching. Epi sa a se jis pou lèt operasyon. Imagine ki jan anpil plis kòd ou ta dwe fè fas a kreye, mete ajou, ak retire itilizatè, travay, ak atansyon. api.ts poller.ts Si ou kouri app a kounye a, ou ta dwe wè ke li travay anpil pi bon. UI a se pi estab epi yo pa fè mal kòm souvan. Ou ka toujou wè kèk erè nan konsole a, men yo ap travay gracefully epi yo pa afekte eksperyans la itilizatè kòm anpil. Dezavantaj nan metòd sa a Plis kodaj boilerplate: Nou te ekri yon anpil nan kòd yo trete erè, retrips, tanperati, annulasyon, ak caching. Sa a ka byen vit vin chaje ak difisil yo kenbe. Pa trè reusab: Kòd la se strikman kouvri ak ka itilize espesifik nou an ak pa trè reusab pou lòt pwojè oswa senaryo. Limite karakteristik: Kòd la sèlman ranplase senaryo erè baz. Senaryo plis konplèks tankou eksponansyèl backkoff, circuit breakers, oswa jesyon erè mondyal pral mande pou menm plis kòd. Sèvi Pou pi bon manyen Fetch Pwodwi Pwodwi Pou rezoud dezavantaj yo nan manyen kout nou an, mwen te ekri yon bibliyotèk rele Li se yon bibliyotèk ti ak limyè ki enkli API a Fetch epi yo bay yon fason senp ak deklaratif pou travay ak erè, retrips, tanperati, anons, ak kèk plis karakteristik. ffetch Tanpri rescrete logik nou an lè l sèvi avèk Ou ka jwenn kòd la sou branch nan repo a, se konsa ou ka tcheke li ak . ffetch ffetch git checkout ffetch Premye, enstale nan Pwodwi yo: ffetch frontend npm install @gkoos/ffetch Lè sa a, nou ka rescrete nou an Dosye itilize : poller.ts ffetch import createClient from '@gkoos/ffetch'; // Cache for responses const cache: Record<string, any> = {}; // Create ffetch client const api = createClient({ timeout: 3000, retries: 2, }); function cancelRequests() { api.abortAll(); } export async function fetchUserListData() { cancelRequests(); if (cache.userList) return cache.userList; try { const userIds = await api('http://localhost:3000/users').then(r => r.json()); const users = await Promise.all( userIds.map((id: number) => api(`http://localhost:3000/users/${id}`).then(r => r.json())) ); const taskCounts = await Promise.all( userIds.map((id: number) => api(`http://localhost:3000/users/${id}/tasks`).then(r => r.json()).then((tasks: any[]) => tasks.length)) ); cache.userList = { users, taskCounts }; return cache.userList; } catch (err) { if (cache.userList) return cache.userList; throw err; } } export async function fetchUserDetailsData(userId: number) { cancelRequests(); const cacheKey = `userDetails_${userId}`; if (cache[cacheKey]) return cache[cacheKey]; try { const user = await api(`http://localhost:3000/users/${userId}`).then(r => r.json()); const taskIds = await api(`http://localhost:3000/users/${userId}/tasks`).then(r => r.json()); const tasks = await Promise.all( taskIds.map((id: number) => api(`http://localhost:3000/tasks/${id}`).then(r => r.json())) ); cache[cacheKey] = { user, tasks }; return cache[cacheKey]; } catch (err) { if (cache[cacheKey]) return cache[cacheKey]; throw err; } } Kòd la se anpil pi limyè ak pi fasil yo li. Nou pa gen okenn pwoblèm sou retrips, tan-outs, oswa anilasyon ankò. nou jis kreye yon kliyan ak opsyon yo deside epi sèvi ak li fè demann. ffetch Yon lòt benefis nan sèvi ak ffetch Circuit breaker: otomatik refwadisman pousantaj fini apre repete erè Otomatik eksponansyèl backoff pou retrips: ogmante tan repo ant retrips Global manyen erè: hooks pou logging, modifye demann / repons, elatriye Pou egzanp, nou ka chwazi retounen sou erè rezo ak erè 5xx sèvè, men pa sou erè 4xx kliyan. pa fè anyen magik ou pa te kapab bati tèt ou, men li sove ou soti nan ekri, tès, ak kenbe tout sa a boilerplate. Li se yon wrapper konpetans ki bak nan modèl-klas pwodiksyon (tankou circuit breaker ak backoff) se konsa ou ka konsantre sou app ou, pa logik la retire ou. Li tou parèt nan lay la retire, se konsa ou ka toujou sèvi ak pwòp caching ou, jesyon eta, ak bibliyotèk UI jan ou wè apwopriye. ffetch Konklisyon prensipal la nan atik sa a se pa ke ou ta dwe itilize Espesifikman, men ke ou pa ta dwe depann sou vanilla Fetch pou aplikasyon pwodiksyon-kòmanse. Rezo a se pa serye, epi ou bezwen yo dwe pare pou li. Ou bezwen fè fas ak erè, retries, timeouts, annulations, ak caching graceously bay yon bon eksperyans itilizatè. ffetch Tout sa ou bezwen fè depann sou ka itilizasyon espesifik ou ak kondisyon, men ou pa ka ale nan pwodiksyon ki ap travay sèlman sou wout la satisfè. Tout bagay ka epi yo pral ale mal, ak app ou bezwen fè fas a nan omwen senaryo ki pi komen. Epi Èske ou ka ede ak sa. ffetch