JavaScript Crash Course

Асинхронність в JavaScript

9

Зміст:

  1. Що таке асинхронність

  2. Promise

  3. Робота із запитами. Fetch API

  4. Відловлювання помилок із try...catch

  5. HTTP методи на статус коди

Що таке асинхронність?

Асинхронність

Це процес обробки введення/виводу, що дозволяє продовжити обробку інших завдань, не чекаючи завершення попереднього завдання.

Асинхронність в JS - це як шеф-повар, який може готувати декілька страв одночасно

А браузер - це офіціант, який буде приносити всі старви клієнту вчасно

Асинхронність в JS

Багато функцій, які надаються браузерами, і особливо найцікавіші з них, потенційно можуть зайняти багато часу, а тому є асинхронними. Наприклад:

  • HTTP-запитів за допомогою fetch()
  • доступ до камери або мікрофона користувача за допомогою getUserMedia()
  • вібір файлів для доступу за допомогою showOpenFilePicker()

Promise

Що таке Promise?

Об’єкт Promise представляє можливе завершення (або збій) асинхронної операції та її результат.

Стани Promise

Promise може знаходиться в одному з таких станів:

  • pending: початковий стан, не виконано і не відхилено.
  • fulfilled: це означає, що операція була успішно завершена.
  • rejected: це означає, що операція не вдалася.
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo');
  }, 300);
});

myPromise
  .then(handleResolvedA, handleRejectedA)
  .then(handleResolvedB, handleRejectedB)
  .then(handleResolvedC, handleRejectedC)
  .catch(handleRejectedAny);

Оскільки методи Promise.prototype.then() і Promise.prototype.catch() повертають Promise, їх можна зв’язати(chained).

Метод Promise.all(iterable) повертає проміс, який виконається тоді, коли будуть виконані всі проміси, передані у вигляді аргументу, що перераховується, або відхилено будь-який з переданих промісів.

Promise.all()

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

Метод Promise.allSettled() повертає проміс, який виконується, коли всі отримані проміси завершені (виконані або відхилені), що містить масив результатів виконання отриманих промісів.

Promise.allSettled()

const promise1 = Promise.resolve(3);
const promise2 = new Promise(
  (resolve, reject) => setTimeout(reject, 100, 'foo')
);
const promises = [promise1, promise2];

Promise.allSettled(promises)
  .then((results) =>
       results.forEach((result) => console.log(result.status))
   );

// expected output:
// "fulfilled"
// "rejected"
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));

const promises = [promise1, promise2, promise3];

Promise.any(promises).then((value) => console.log(value));

// expected output: "quick"

Promise.any() приймає ітерабельний об'єктів Promise і  повертає перший успішний promise. Якщо жоден promise в ітерації не виконуються, то отримуємо AggregateError

Promise.any()

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"

Метод Promise.any() приймає об'єкт, що містить об'єкти промісів Promise. Як тільки один із промісів (Promise) виконається успішно (fullfill), метод поверне єдиний об'єкт Promise зі значенням виконаного промісу. Якщо жоден із промісів не завершиться успішно (якщо всі проміси завершаться з помилкою, тобто rejected), тоді повернутий об'єкт Promise буде відхилений (rejected) з одним із значень: масив, що містить причини помилки (відхилення), або AggregateError — підклас Error що об'єднує викинуті помилки разом.

Promise.race()

Promise.reject(new Error('fail')).then(function() {
  // not called
}, function(error) {
  console.error(error); // Stacktrace
});

Метод Promise.reject(reason) повертає об'єкт Promise, який був відхилений з цієї причини.

Promise.reject()

Promise.resolve('Success').then(function(value) {
  console.log(value); // "Success"
}, function(value) {
  // not called
});

Метод Promise.resolve(value) повертає Promise виконаний із переданим значенням. Якщо передане значення є thenable - об'єкт (тобто має метод "then" method), повертається проміс буде слідувати thenable - об'єкту, приймаючи свій стан; в іншому випадку повертається проміс буде виконаний з переданим значенням.

Promise.resolve()

Робота із запитами. Fetch API

Взаємодія клієнт-сервер

user

website

server

взаємодія

HTTP запит

HTTP відповідь

Fetch API

Fetch API надає інтерфейс для отримання ресурсів (у тому числі через мережу). Він здасться знайомим будь-кому, хто використовував XMLHttpRequest, але новий API є більш потужним та гнучким набором функцій.

Fetch інтерфейс

  • fetch() - метод для запитів ресурсів
  • Headers - заголовки response/request headers, що дозволяють виконувати різні дії в залежності від результату
  • Request - запит ресурсів
  • Response - результат запиту

 

Приклад

const USERS_URL = "https://api.github.com/users";

fetch(USERS_URL)
	.then(response => response.json())
	.then(result => console.log(result));

Приклад із відловленням помилки

const USERS_URL = "https://api.github.com/users";

fetch(USERS_URL)
	.then(response => response.json())
	.then(result => console.log(result))
    .catch(error => {
        // handle the error
        console.error(error);
    });

async ... await

Асинхронна функція — це функція, оголошена за допомогою ключового слова async, і ключового слова await. Ключові слова async і await дозволяють записати асинхронну поведінку на основі promise у більш чистому стилі, уникаючи необхідності явного налаштування ланцюжків promises.

Асинхронні функції також можуть бути визначені як вирази.

Приклад

async function fetchText() {
    let response = await fetch('/readme.txt');

    console.log(response.status); // 200
    console.log(response.statusText); // OK

    if (response.status === 200) {
        let data = await response.text();
        // handle data
    }
}

fetchText();

Приклад

async function foo() {
  return 1;
}

// it is similar to:

function foo() {
  return Promise.resolve(1);
}

Приклад

function resolveAfter2Seconds() {
  console.log("starting slow promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("slow promise is done");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("starting fast promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("fast promise is done");
    }, 1000);
  });
}

async function sequentialStart() {
  // 1. Execution gets here almost instantly
  const slow = await resolveAfter2Seconds();
  console.log(slow); // 2. this runs 2 seconds after 1.

  const fast = await resolveAfter1Second();
  console.log(fast); // 3. this runs 3 seconds after 1.
}

Відловлювання помилок із try...catch

 

try...catch

try {
  throw new Error('oops');
} catch (ex) {
  console.error('inner', ex.message);
} finally {
  console.log('finally');
}

fetch + try...catch

fetch(url)
  .then((response) => {
    if (response.ok) {
      return response.json();
    }

    throw new Error('Something went wrong');
  })
  .then((responseJson) => {
    // Do something with the response
  })
  .catch((error) => {
    console.log(error)
  });

HTTP методи на статус коди

HTTP методи

GET

DELETE

POST

PUT

PATCH

OPTIONS

HTTP методи: GET

fetch("http://example.com/movies.json")
	.then(res => res.json())
	.then(resp => console.log(resp));

GET метод використовується для отримання даних

HTTP методи: POST

fetch("https://example.com/answer", {
      method: "POST", // *GET, POST, PUT, DELETE, etc.
      mode: "cors", // no-cors, *cors, same-origin
      cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      credentials: "same-origin", // include, *same-origin, omit
      headers: {
        "Content-Type": "application/json",
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      redirect: "follow", // manual, *follow, error
      referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, ...
      body: JSON.stringify(data), // body data type must match "Content-Type" header
  })
  .then(res => res.json())
  .then(resp => console.log(resp));

POST метод використовується для створення нових даних

HTTP методи: POST

fetch("https://example.com/answer", {
      method: "POST", // *GET, POST, PUT, DELETE, etc.
      mode: "cors", // no-cors, *cors, same-origin
      cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      credentials: "same-origin", // include, *same-origin, omit
      headers: {
        "Content-Type": "application/json",
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      redirect: "follow", // manual, *follow, error
      referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, ...
      body: JSON.stringify({ answer: 42 }), // body data type must match "Content-Type"
  })
  .then(res => res.json())
  .then(resp => console.log(resp));

POST метод використовується для створення нових даних

HTTP методи: PUT

fetch("https://example.com/answer", {
      method: "PUT", // *GET, POST, PUT, DELETE, etc.
      headers: {
        "Content-Type": "application/json",
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: JSON.stringify({ answer: 42 }),
  })
  .then(res => res.json())
  .then(resp => console.log(resp));

PUT метод використовується для оновлення існуючих даних

HTTP методи: PUT

async function upload(formData) {
  try {
    const response = await fetch("https://example.com/profile/avatar", {
      method: "PUT",
      body: formData,
    });
    const result = await response.json();
    console.log("Success:", result);
  } catch (error) {
    console.error("Error:", error);
  }
}

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append("username", "abc123");
formData.append("avatar", fileField.files[0]);

upload(formData);

PUT метод можна використовувати для завантаження даних:

HTTP методи: PATCH

fetch("https://example.com/answer", {
      method: "PATCH", // *GET, POST, PUT, DELETE, etc.
      headers: {
        "Content-Type": "application/json",
        // 'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: JSON.stringify({ answer: 42 }),
  })
  .then(res => res.json())
  .then(resp => console.log(resp));

PATCH метод використовується для часткового оновлення існуючих даних

HTTP методи: DELETE

fetch("https://example.com/answer/answer-id-1", {
      method: "DELETE"
      headers: {
        "Content-Type": "application/json",
      },
  })
  .then(res => res.json())
  .then(resp => console.log(resp));

DELETE метод використовується для видалення існуючих даних

HTTP статус коди

201

404

200

409

403

401

500

302

HTTP статус коди

Виділяють п’ять класів:

  1. 1xx — інформаційні коди. Вони відповідають за процес передачі даних. Це тимчасові коди, котрі інформують про те, що запит прийнято і обробка буде продовжуватися.
  2. 2xx — успішна обробка. Запит було отримано та успішно оброблено сервером.
  3. 3xx — перенаправлення (редирект). Ці відповіді свідчать про те, що необхідно вжити подальших дій для виконання запиту. Наприклад, зробити запит на іншу адресу.
  4. 4xx — помилка клієнта. Означає, що запит не може бути виконаний з вини користувача.
  5. 5xx — помилка сервера. Ці коди виникають через помилки на стороні сервера. У цьому випадку користувач все зробив правильно, але сервер не може виконати запит. Для кодів цього класу сервер обов’язково показує повідомлення, що не може обробити запит і з якої причини.

HTTP статус коди

Основні:

  • 200 — запит виконано успішно
  • 301 — сторінка переміщена і виконується редирект
  • 401 — аутентифікація HTTP не вдалася (Unauthorized)
  • 403 — відмовлено в доступі (Forbidden)
  • 404 — означає, що за вказаною URL нічого не знайдено (Not Found)
  • 409 — спроба створити вже існуючі дані (Conflict)
  • 500 — внутрішня помилка сервера (Іnternal Server Error)
  • 503 — сервер тимчасово не може опрацьовувати запити з технічних причин (Service Unavailable)
  • 504 — сервер працював як проксі і не дочекався відповіді від вищого сервера для завершення запиту (Gateway Timeout)

Q & A

_11 JavaScript Crash Course

By Inna Ivashchuk

_11 JavaScript Crash Course

  • 397