Гузариш ба мундариҷа
Мавзӯи 19 аз 21
Назария

async/await ва fetch

Ин барои чӣ лозим аст

Промисҳо «дӯзахи callback-ҳо»-ро ҳал карданд, вале занҷирҳои then нисбат ба коди оддӣ бадтар хонда мешаванд. Калимаҳои калидии async ва await имкон медиҳанд, ки коди асинхрониро гӯё синхронӣ нависем: аз боло ба поён, бо тағйирёбандаҳои оддӣ ва try … catch-и оддӣ.

fetch бошад — он чизест, ки ҳамааш барои он сар шуда буд: дархостҳо ба сервер. Ҳар сайте, ки обу ҳаво, навори хабарҳо, молҳо ё паёмҳоро нишон медиҳад, онҳоро тавассути дархостҳо мегирад. Дар React додаҳо маҳз ҳамин тавр бор мешаванд: useEffect + fetch + await.

Функсияи async

Калимаи async пеш аз функсия ду кор мекунад:

  1. функсия ҳамеша промис бармегардонад: return 5 ба промис бо қимати 5 табдил меёбад;
  2. дар дохил await навиштан мумкин аст.
async function getNumber() {
  return 5;
}

const p = getNumber();
console.log(p instanceof Promise);
p.then((value) => console.log(value));

Натиҷа:

true
5

Функсияҳои тирчадор ҳам асинхронӣ мешаванд: const load = async (id) => { ... };

await — промисро интизор шудан

await промис ҳамин функсия-ро то иҷро шудани промис бозмедорад ва қимати онро бармегардонад:

const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));

async function main() {
  console.log("Сар кардем");
  const name = await wait(100, "Аня");          // 100 мс интизор мешавем
  const city = await wait(100, "Душанбе");      // боз 100 мс
  console.log(name + " аз шаҳри " + city);
}

main();
console.log("main оғоз шуд, вале ҳоло тамом нашудааст");

Натиҷа:

Сар кардем
main оғоз шуд, вале ҳоло тамом нашудааст
Аня аз шаҳри Душанбе

Муҳим: await тамоми барномаро қатъ намекунад. Вай танҳо функсияи async-и худро таваққуф медиҳад, ва коди боқимонда идома меёбад. Барои ҳамин сатри дуюм пеш аз сатри сеюм чоп шуд.

Ин ҳамон коди занҷири then аст, танҳо дигар хел навишта шудааст: ҳама чиз баъд аз await — гӯё дар дохили then.

Хатоҳо: try … catch

Агар промиси зери await рад шавад, await хаторо мепартояд, мисли throw. Онро бо try … catch-и оддӣ медоранд:

const fail = () => new Promise((_, reject) => setTimeout(() => reject(new Error("сервер дастнорас аст")), 50));

async function main() {
  try {
    await fail();
    console.log("ба ин ҷо намерасем");
  } catch (err) {
    console.log("Хато: " + err.message);
  } finally {
    console.log("Боркунӣ тамом шуд");
  }
}

main();

Натиҷа:

Хато: сервер дастнорас аст
Боркунӣ тамом шуд

Пай дар пай ё параллелӣ

Ду await паси ҳам пай дар пай иҷро мешаванд: дархости дуюм танҳо вақте сар мешавад, ки аввалӣ тамом шуд. Агар дархостҳо аз ҳамдигар вобаста набошанд, ин интизории зиёдатӣ аст. Онҳоро якбора оғоз кунед ва бо Promise.all якҷоя интизор шавед:

const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));

async function sequential() {
  const a = await wait(100, "A");
  const b = await wait(100, "B");
  return [a, b];
}

async function parallel() {
  const [a, b] = await Promise.all([wait(100, "A"), wait(100, "B")]);
  return [a, b];
}

async function main() {
  let start = Date.now();
  await sequential();
  console.log("Пай дар пай зиёда аз 190 мс: " + (Date.now() - start >= 190));
  start = Date.now();
  await parallel();
  console.log("Параллелӣ камтар аз 190 мс: " + (Date.now() - start < 190));
}

main();

Натиҷа:

Пай дар пай зиёда аз 190 мс: true
Параллелӣ камтар аз 190 мс: true

Қоида: агар ба қадами навбатӣ натиҷаи қадами пешина лозим бошад — await пай дар пай. Агар не — Promise.all.

await дар давра

for (const id of ids) {
  const user = await loadUser(id);   // пай дар пай: ҳар кадом пешинаро интизор мешавад
}

const users = await Promise.all(ids.map((id) => loadUser(id)));   // ҳама якбора

Аммо ids.forEach(async (id) => { await ... }) — дом аст: forEach callback-ҳои асинхрониро интизор намешавад, ва коди баъд аз он пеш аз тамом шудани боркуниҳо иҷро мешавад.

fetch — дархост ба сервер

fetch(суроға) дархости HTTP мефиристад ва промиси ҷавоб (Response)-ро бармегардонад. Бадани ҷавоб алоҳида ва ҳам асинхронӣ хонда мешавад — барои ҳамин await ду бор:

async function loadTodo() {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  console.log(response.status);       // 200 — рамзи ҷавоб
  console.log(response.ok);           // true, агар рамз 200–299 бошад
  const todo = await response.json(); // бадани ҷавоб: JSON → объект
  console.log(todo.title);
}

loadTodo();

Натиҷа тақрибан чунин аст (ин сервери воқеии таълимӣ аст):

200
true
delectus aut autem

Ҷавоб чӣ дорад:

Хосият / усул Чӣ медиҳад
response.status рамзи ҷавоб: 200, 201, 404, 500…
response.ok true, агар рамз аз 200 то 299 бошад
await response.json() бадан, аз JSON таҳлилшуда
await response.text() бадан ҳамчун сатр
response.headers.get("Content-Type") сарлавҳаи ҷавоб

Рамзҳое, ки бештар вомехӯранд: 200 — ҳама хуб, 201 — сохта шуд, 400 — дархости нодуруст, 401 / 403 — дастрасӣ нест, 404 — ёфт нашуд, 500 — хато дар сервер.

Хатои шабака ва хатои сервер — чизҳои гуногунанд

fetch промисро танҳо вақте рад мекунад, ки ҷавоб тамоман нест: интернет нест, сервер дастнорас аст, суроға нодуруст аст. Ҷавоби 404 ё 500 — ин ҷавоби бомуваффақият гирифташуда бо рамзи бад аст. Онро худатон санҷидан лозим:

async function loadJSON(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

Бе санҷиши response.ok барнома кӯшиш мекунад, ки саҳифаи хаторо ҳамчун додаҳо таҳлил кунад.

Фиристодани додаҳо: POST

Барои фиристодани додаҳо (сохтани вазифа, ворид шудан, гузоштани шарҳ) ҳамчун аргументи дуюм танзимоти дархостро медиҳанд:

async function addComment(text) {
  const response = await fetch("https://api.example.com/comments", {
    method: "POST",                                    // пешфарз GET
    headers: { "Content-Type": "application/json" },   // ба сервер мегӯем: дар бадан JSON
    body: JSON.stringify({ text }),                    // бадан — сатр
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();   // сервер одатан объекти сохташударо мефиристад
}

Усулҳои асосии HTTP: GET — гирифтан, POST — сохтан, PUT / PATCH — тағйир додан, DELETE — нест кардан. Якҷоя онҳо REST API-ро ташкил медиҳанд — аксари серверҳое, ки шумо бо онҳо кор мекунед, ҳамин тавр сохта шудаанд.

Боркунии маъмулии додаҳо

Ин қолабест, ки дар ҳар барнома вомехӯрад — аз ҷумла дар React. Боркунӣ се ҳолат дорад: меравад, шуд, хато:

async function showUsers() {
  console.log("Боркунӣ...");
  try {
    const users = await loadJSON("https://jsonplaceholder.typicode.com/users");
    console.log("Корбарон: " + users.length);
  } catch (err) {
    console.log("Бор кардан нашуд: " + err.message);
  } finally {
    console.log("Нишондиҳандаи боркуниро пинҳон мекунем");
  }
}

Дар React ин се ҳолат се тағйирёбандаи ҳолат мешаванд: loading, data ва error.

Дар дохил чӣ гуна кор мекунад

async/await — ин «шакари синтаксисӣ» бар промисҳост. Ба await дучор шуда, муҳаррик ҷои истодани функсияро дар хотир нигоҳ медорад ва аз он мебарояд, промис баргардонда. Коди боқимонда кор карданро идома медиҳад. Вақте промиси интизоршаванда иҷро шуд, давомаи функсия ба навбати микровазифаҳо гузошта мешавад ва аз ҳамон ҷо, бо ҳамон тағйирёбандаҳо иҷро мешавад.

fetch худи дархостро бо қувваи браузер ё Node.js — берун аз JavaScript иҷро мекунад. Дар браузер маҳдудияти CORS ҳаст: саҳифа ҷавобҳои сервери бегонаро танҳо вақте хонда метавонад, ки он сервер инро бо сарлавҳаҳои махсус иҷозат дода бошад. Барои ҳамин баъзан дархосте, ки дар Node.js кор мекунад, дар браузер хато медиҳад.

Хатоҳои маъмул

  1. await-и фаромӯшшуда. const user = fetch(url); — дар user промис аст, на ҷавоб. response.json() бе await ҳам промис аст, на додаҳо.
  2. await берун аз функсияи async. Дар функсияи оддӣ await хатои синтаксисӣ аст. (Дар модулҳо ва дар вазифаҳои ин курс await дар сатҳи боло ҳам иҷозат аст.)
  3. Санҷиши response.ok нест. Ҷавоби 404 худ аз худ ба catch намеафтад.
  4. Пайдарпайии зиёдатӣ. Дархостҳои мустақил бо await паси ҳам — суст аст. Promise.all-ро истифода баред.
  5. forEach бо async. Интизор намешавад. for … of ё Promise.all бо map-ро истифода баред.
  6. JSON.stringify барои body фаромӯш шуд. Объектро мустақим додан мумкин нест — дар бадан танҳо сатр.

Ёддошт

Чӣ Чӣ тавр
Функсияи асинхронӣ async function f() { ... }, async () => { ... }
Интизор шудан const x = await promise;
Хато try { await ... } catch (err) { ... }
Параллелӣ const [a, b] = await Promise.all([p1, p2]);
GET const res = await fetch(url); const data = await res.json();
Санҷиш if (!res.ok) throw new Error(HTTP ${res.status});
POST fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(obj) })

Номи корбар

Осон+10 XP

Функсияи async-и getUserName(id)-ро нависед. Вай дархости fetch-ро ба суроғаи https://api.example.com/users/<id> мефиристад, ҷавобро ҳамчун JSON таҳлил мекунад (ин объекти { id, name } аст) ва name-ро бармегардонад.

await getUserName(1)   → "Ann"
await getUserName(2)   → "Timur"

Барои ҳал кардани масъалаҳо коннекторро пайваст кун

Коди масъалаҳо дар компютери худат иҷро мешавад — ин аз браузер тезтар ва боэътимодтар аст. Барои ин барномаи ройгони коннектори CodeWithMe лозим аст. Онро як бор пайваст кардан кифоя аст.

Коннекторро дар ин компютер ҷустуҷӯ мекунем…

  1. 1
    Коннекторро барои системаи худ (Windows, macOS ё Linux) зеркашӣ кун.Зеркашии коннекторДар саҳифаи зеркашӣ файлеро интихоб кун, ки дар номаш системаи ту ҳаст, масалан windows-amd64.
  2. 2Онро иҷро кун ва ҷузвдонеро интихоб кун, ки ҳалҳоят дар он ҷо меистанд. Python ва Node.js-ро ӯ худаш ҳангоми зарурат ба он ҷо мегузорад.
  3. 3
    Дар поён рамзи 6-рақамаро ворид кун — он дар равзанаи коннектор навишта шудааст («Рамз барои сайти CodeWithMe»). Агар браузер дар бораи дастрасӣ ба шабакаи маҳаллӣ пурсад — иҷозат деҳ.

Коннектор кушода аст (рамзи манбаъ дар GitHub), танҳо дар компютери ту кор мекунад ва кодро танҳо пас аз ворид кардани рамзи пайвастшавӣ иҷро мекунад.