Асинхронність в JavaScript
Що таке асинхронність
Promise
Робота із запитами. Fetch API
Відловлювання помилок із try...catch
HTTP методи на статус коди
Це процес обробки введення/виводу, що дозволяє продовжити обробку інших завдань, не чекаючи завершення попереднього завдання.
Асинхронність в JS - це як шеф-повар, який може готувати декілька страв одночасно
А браузер - це офіціант, який буде приносити всі старви клієнту вчасно
Багато функцій, які надаються браузерами, і особливо найцікавіші з них, потенційно можуть зайняти багато часу, а тому є асинхронними. Наприклад:
Що таке Promise?
Об’єкт Promise представляє можливе завершення (або збій) асинхронної операції та її результат.
Стани Promise
Promise може знаходиться в одному з таких станів:
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()
user
website
server
взаємодія
HTTP запит
HTTP відповідь
Fetch API надає інтерфейс для отримання ресурсів (у тому числі через мережу). Він здасться знайомим будь-кому, хто використовував XMLHttpRequest, але новий API є більш потужним та гнучким набором функцій.
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 дозволяють записати асинхронну поведінку на основі 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 {
throw new Error('oops');
} catch (ex) {
console.error('inner', ex.message);
} finally {
console.log('finally');
}
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)
});
GET
DELETE
POST
PUT
PATCH
OPTIONS
fetch("http://example.com/movies.json")
.then(res => res.json())
.then(resp => console.log(resp));
GET метод використовується для отримання даних
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 метод використовується для створення нових даних
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 метод використовується для створення нових даних
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 метод використовується для оновлення існуючих даних
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 метод можна використовувати для завантаження даних:
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 метод використовується для часткового оновлення існуючих даних
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 метод використовується для видалення існуючих даних
201
404
200
409
403
401
500
302
Виділяють п’ять класів:
Основні: