Loading...

async/wait

async/wait

 

"Асинхрондук/күтүү" деп аталган убадалар менен иштөө үчүн атайын синтаксис бар. Аны түшүнүү жана колдонуу таң калыштуу оңой.

Асинхрондук функциялар

Ачкыч сөз менен баштайлы async. Ал функциянын алдына төмөнкүдөй жайгаштырылат:

@A@async function f() {
  return 1;
}@A@

Сөздүн asyncбир жөнөкөй мааниси бар: бул функция ар дайым убаданы кайтарат. Башка түрдөгү баалуулуктар автоматтык түрдө ийгиликтүү убадага оролгон.

Мисалы, бул функция аткарылган убаданы натыйжа менен кайтарат 1:

@A@async function f() {
  return 1;
}

f().then(alert); // 1@A@

Сиз убаданы ачык кайтара аласыз, натыйжасы бирдей болот:

@A@async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1@A@

asyncОшентип , функциянын алдындагы ачкыч сөз функция баары бир убаданы кайтарып берерин камсыздайт. Макул, жетиштүү жөнөкөй? Бирок бул баары эмес. Дагы бир ачкыч сөз бар await, аны -функциялардын ичинде гана колдонсо болот async.

Await

Синтаксис:

@A@// работает только внутри async–функций
let value = await promise;@A@

Ачкыч сөз awaitJavaScript котормочусун оң жактагы убада awaitаткарылганга чейин күтүшүнө себеп болот. Андан кийин, ал өз натыйжасын кайтарып берет жана коддун аткарылышы улантылат.

Бул мисалда, убада 1 секунданын ичинде ишке ашат:

@A@async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("готово!"), 1000)
  });

  let result = await promise; // будет ждать, пока промис не выполнится (*)

  alert(result); // "готово!"
}

f();@A

Бул мисалда, функциянын аткарылышы (*)убада аткарылмайынча сапта токтойт. Бул функция башталгандан бир секунддан кийин болот. Андан кийин, resultубаданын аткарылышынын натыйжасы өзгөрмөгө жазылат, ал эми браузер "бүттү!" эскертүү терезесин көрсөтөт.

Ал JavaScriptти убаданын аткарылышын күтүүгө мажбурласа да await, CPU ресурстарын талап кылбайт. Убада аткарылмайынча, JS кыймылдаткычы башка тапшырмаларды аткара алат: башка скрипттерди аткаруу, окуяларды башкаруу ж.б.

Чынында, бул жөн гана убаданын натыйжасын алуу үчүн "синтаксистик кант" , караганда сүрөттөмө promise.then.

awaitкадимки функцияларда колдонууга болбойт

awaitЭгер биз билдирилген функциянын ичинде колдонууга аракет кылсак async, синтаксис катасын алабыз:

@A@function f() {
  let promise = Promise.resolve(1);
  let result = await promise; // SyntaxError
}@A

asyncФункцияны жарыялоонун алдында ачкыч сөздү көрсөтсөк, ката болбойт . Мурда айтылгандай, awaitаны -функциялардын ичинде гана колдонсо болот async.

Келгиле , Убадаларды чынжырлоодонshowAvatar() мисалды кайра жазалы :async/await

  1. Биз чалуулар .thenменен алмаштыруу керек await.
  2. asyncЖана функция декларациясынын алдында ачкыч сөздү кошуңуз .
@A@async function showAvatar() {

  // запрашиваем JSON с данными пользователя
  let response = await fetch('/article/promise-chaining/user.json');
  let user = await response.json();

  // запрашиваем информацию об этом пользователе из github
  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
  let githubUser = await githubResponse.json();

  // отображаем аватар пользователя
  let img = document.createElement('img');
  img.src = githubUser.avatar_url;
  img.className = "promise-avatar-example";
  document.body.append(img);

  // ждём 3 секунды и затем скрываем аватар
  await new Promise((resolve, reject) => setTimeout(resolve, 3000));

  img.remove();

  return githubUser;
}

showAvatar();@A@

Бул абдан жөнөкөй жана окула турган болуп чыкты, туурабы? Мурдагыдан алда канча жакшы.

awaitжогорку уя деңгээлинде колдонууга болбойт

Программисттер, жөнүндө билип await, көп учурда бул функцияны уянын жогорку деңгээлинде колдонууга аракет кылышат (функциянын корпусунан тышкары). Бирок awaitал -функциялардын ичинде гана иштегендиктен async, бул иштебейт:

@A@// SyntaxError на верхнем уровне вложенности
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();

Сиз бул кодду анонимдүү -функцияга ороп алсаңыз болот async, ошондо баары иштейт:

(async () => {
  let response = await fetch('/article/promise-chaining/user.json');
  let user = await response.json();
  ...
})();@A@
await"андан кийин" объектилер менен иштейт

сыяктуу promise.thenawaitубадага шайкеш объекттер менен иштөөгө мүмкүндүк берет. Идея, эгерде сиз объектте методду чакыра алсаңыз then, аны менен колдонуу үчүн жетиштүү болот await.

Төмөнкү мисалда класс инстанциялары Thenableменен иштейт await:

 
 
@A@class Thenable {
  constructor(num) {
    this.num = num;
  }
  then(resolve, reject) {
    alert(resolve);
    // выполнить resolve со значением this.num * 2 через 1000мс
    setTimeout(() => resolve(this.num * 2), 1000); // (*)
  }
};

async function f() {
  // код будет ждать 1 секунду,
  // после чего значение result станет равным 2
  let result = await new Thenable(1);
  alert(result);
}

f();@A@

awaitАл убада кылынбаган объектти алганда , .thenJavaScript бул ыкманы автоматтык түрдө иштетип, ага орнотулган функцияларды resolveжана аргументтерди өткөрүп берет reject. Андан кийин awaitбул функциялардын бири чакырылмайынча коддун андан аркы аткарылышын токтотот (мисалы, бул сап (*)). resolveАндан кийин, коддун аткарылышы натыйжа менен же тиешелүү түрдө уланат reject.

Асинхрондук класс методдору

Асинхрондук ыкманы жарыялоо үчүн, жөн гана asyncаталыштын алдына жазыңыз:

@A@class Waiter {
  async wait() {
    return await Promise.resolve(1);
  }
}

new Waiter()
  .wait()
  .then(alert); // 1@A@

Асинхрондук функциялар сыяктуу эле, бул ыкма убаданы кайтарууга кепилдик берет жана сиз колдоно аласыз await.

Ката иштетүү

Убада ишке ашса, await promiseжыйынтыгын кайтарат. Качан ал ката менен аяктаганда, өзгөчөлүк ташталат. Бул жерде бир сөз бар дегендей throw.

Мындай код:

@A@async function f() {
  await Promise.reject(new Error("Упс!"));
}

Ушул сыяктуу эле нерсени жасайт:

async function f() {
  throw new Error("Упс!");
}@A@

Бирок бир айырма бар: иш жүзүндө убада дароо ишке ашпай калышы мүмкүн, бирок бир нече убакыт өткөндөн кийин. Бул учурда кечигүү болот, андан кийин awaitөзгөчөлүктү таштайт.

try..catchМындай каталар адаттагыдай эле, колдонуу менен кармалышы мүмкүн throw:

@A@async function f() {

  try {
    let response = await fetch('http://no-such-url');
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

f();@A@

Ката болгон учурда, аткаруу tryүзгүлтүккө учурайт жана башкаруу блоктун башына өтөт catch. Блок tryбир нече саптарды ороп алат:

 
 
@A@async function f() {

  try {
    let response = await fetch('/no-user-here');
    let user = await response.json();
  } catch(err) {
    // перехватит любую ошибку в блоке try: и в fetch, и в response.json
    alert(err);
  }
}

f();@A@

Эгерде бизде жок болсо try..catch, асинхрондук функция ишке ашпай калган убаданы кайтарат ( абалында rejected). .catchБул учурда, катаны чечүү үчүн убада ыкмасын колдоно алабыз :

 
 
@A@async function f() {
  let response = await fetch('http://no-such-url');
}

// f() вернёт промис в состоянии rejected
f().catch(alert); // TypeError: failed to fetch // (*)@A@

Эгер сиз кошууну унутуп калсаңыз .catch, анда "Колдонулбаган убада катасы" катасы пайда болот жана бул тууралуу маалымат консолдо көрсөтүлөт. Мындай каталарды Убадалар: Ката иштетүү бөлүмүндө айтылгандай, глобалдык иштеткич кармап алат .

async/awaitЖанаpromise.then/catch

менен иштөөдө async/await.thenал сейрек колдонулат, анткени awaitал автоматтык түрдө убаданын аткарылышын күтөт. Бул учурда, адатта (бирок ар дайым эмес) каталарды колдонуунун try..catchордуна колдонуу алда канча ыңгайлуу .catch.

Бирок уянын жогорку деңгээлинде ( async-функциялардан тышкары) awaitаны колдонууга болбойт, ошондуктан .then/catchакыркы натыйжаны же каталарды чечүү кеңири таралган практика болуп саналат.

(*)Бул жогорудагы мисалдагы сапта жасалат .

async/awaitменен сонун иштейтPromise.all

Бир эле учурда бир нече убадаларды күтүшүңүз керек болгондо, аларды ороп Promise.all, анан await:

@A@// await будет ждать массив с результатами выполнения всех промисов
let results = await Promise.all([
  fetch(url1),
  fetch(url2),
  ...
]);@A@

Ката болгон учурда, ал кадимкидей өткөрүлөт: аткарылбай калган убададан Promise.all. Андан кийин өзгөчөлүк ыргытылат, аны менен туюнтманы ороп кармоого болот try..catch.

Бардыгы

asyncФункцияны жарыялоодон мурун ачкыч сөз :

  1. Аны дайыма убадасын кайтарууга мажбурлайт.
  2. awaitБул функцияны денеде колдонууга мүмкүндүк берет .

Алдын ала убадаланган ачкыч сөз awaitJavaScript анын аягына чейин күтүшүнө себеп болот, андан кийин:

  1. Убада ишке ашпай калса, бир өзгөчөлүк бар эле, ыргытылат throw.
  2. Болбосо убаданын натыйжасы кайтарылат.

Алар биргелешип асинхрондук код жазуу үчүн сонун негизди камсыз кылат. Мындай кодду жазуу жана окуу оңой.

менен иштегенде async/awaitансыз деле кыла алсаңыз да promise.then/catch, кээде сиз дагы эле бул ыкмаларды колдонушуңуз керек (мисалы, уянын жогорку деңгээлинде). Эгер параллелдүү бир нече тапшырмаларды аткаруу керек болсо, awaitайкалышта да сонун иштейт.Promise.all

Tasks

 

Анын ордуна Убада чынжырчасынын мисалдарынын бирин кайра жазыңыз :async/await.then/catch

@A@function loadJson(url) {
  return fetch(url)
    .then(response => {
      if (response.status == 200) {
        return response.json();
      } else {
        throw new Error(response.status);
      }
    })
}

loadJson('no-such-user.json') // (3)
  .catch(alert); // Error: 404@A@
чечим
 

Төмөндө Убада чынжыр бөлүгүнөн мисал келтирилген , аны async/awaitордуна колдонуп кайра жазыңыз .then/catch.

Функцияда demoGithubUserрекурсияны цикл менен алмаштырыңыз: колдонуп async/await, муну жасоо оңой болот.

@A@class HttpError extends Error {
  constructor(response) {
    super(`${response.status} for ${response.url}`);
    this.name = 'HttpError';
    this.response = response;
  }
}

function loadJson(url) {
  return fetch(url)
    .then(response => {
      if (response.status == 200) {
        return response.json();
      } else {
        throw new HttpError(response);
      }
    })
}

// Запрашивать логин, пока github не вернёт существующего пользователя.
function demoGithubUser() {
  let name = prompt("Введите логин?", "iliakan");

  return loadJson(`https://api.github.com/users/${name}`)
    .then(user => {
      alert(`Полное имя: ${user.name}.`);
      return user;
    })
    .catch(err => {
      if (err instanceof HttpError && err.response.status == 404) {
        alert("Такого пользователя не существует, пожалуйста, повторите ввод.");
        return demoGithubUser();
      } else {
        throw err;
      }
    });
}

demoGithubUser()@A@;
чечим
 

"Нормалдуу" функция бар. Анын ичиндеги -функцияны аткаруунун натыйжасын кантип алууга болот async?

@A@async function wait() {
  await new Promise(resolve => setTimeout(resolve, 1000));

  return 10;
}

function f() {
  // ...что здесь написать?
  // чтобы вызвать wait() и дождаться результата "10" от async–функции
  // не забывайте, здесь нельзя использовать "await"
}@A@

PS Техникалык жактан алганда, тапшырма абдан жөнөкөй, бирок бул суроо көп учурда асинхрондоштуруу / күтүү менен жакында таанышкан иштеп чыгуучулар тарабынан берилет.