https://gist.github.com/dSalieri/9bbfad303842f9e4800337894e5cbc1c
Колбэк — это первый способ обработать какое-либо асинхронное действие.
Промисы придумали, чтобы организовывать асинхронный код последовательно.
Промис (Promise) — специальный объект JavaScript, который используется для написания и обработки асинхронного кода.
Асинхронные функции возвращают объект Promise в качестве значения. Внутри промиса работает асинхронная операция, которая управляет его состоянием.
Промис может находиться в одном из трёх состояний:
- pending — стартовое состояние, операция стартовала;
- fulfilled — получен результат;
- rejected — ошибка.
Поменять состояние можно только один раз: перейти из pending либо в fulfilled, либо в rejected:
Промис создаётся с помощью конструктора.
В конструктор передаётся функция-исполнитель асинхронной операции (англ. executor). Она вызывается сразу после создания промиса. Задача этой функции — выполнить асинхронную операцию и перевести состояние промиса в fulfilled (успех) или rejected (ошибка).
const promise = new Promise(function (resolve, reject) {
// делаем асинхронную операцию
const data = resolve(data)
})
promise.then((data) => console.log(data))
.catch(() => console.log('error'))
const errorPromise = new Promise(function (resolve, reject) {
reject(new Error('ошибка')) // переводим промис в состояние rejected и отдаём наружу ошибку
})
errorPromise.catch((error) => console.log(error))Промис устроен таким образом, что рычаги управления его состоянием остаются у асинхронной функции. После создания, промис находится в состоянии ожидания pending. Когда асинхронная операция завершается, функция переводит промис в состояние успеха fulfilled или ошибки rejected.
С помощью методов then(), catch() и finally() мы можем реагировать на изменение состояния промиса и выполнять код.
Метод then() присутствует во всех промисах и принимает два аргумента. Первый аргумент — это функция, вызываемая при выполнении обещания, называемая обработчиком выполнения (Fulfilled handler). В эту функцию передаются любые дополнительные данные, связанные с асинхронной операцией.
Второй аргумент — это функция, вызываемая при отклонении промиса, называемая обработчиком отклонения (Rejected handler).
const promise = fetch("http://jsonplaceholder.typicode.com/posts");
promise.then(response => {
// fulfillment
console.log(response.status);
}, reason => {
// rejection
console.error(reason.message);
});Метод catch() используют, чтобы выполнить код в случае ошибки при выполнении асинхронной операции.
Метод catch() принимает в качестве аргумента функцию-колбэк, которая выполняется сразу после того, как промис поменял состояние на rejected. Параметр колбэка содержит экземпляр ошибки:
const promise = fetch("http://jsonplaceholder.typicode.com/posts");
promise.catch(reason => {
// rejection
console.error(reason.message);
});
// is the same as:
promise.then(null, reason => {
// rejection
console.error(reason.message);
});Метод finally() используют, чтобы выполнить код при завершении асинхронной операции. Он будет выполнен вне зависимости от того, была ли операция успешной или завершилась ошибкой.
Метод finally() принимает в качестве аргумента функцию-колбэк, которая выполняется сразу после того, как промис поменял состояние на rejected или fulfilled
const promise = fetch("http://jsonplaceholder.typicode.com/posts");
promise.finally(() => {
// no way to know if fulfilled or rejected console.log("Settled");
});
// is similar to:
const callback = () => {
console.log("Settled");
};
promise.then(callback, callback);const appElement = document.getElementById("app");
const promise = fetch("http://jsonplaceholder.typicode.com/posts");
appElement.classList.add("loading");
promise.then(() => {
// handle success
});
promise.catch(() => {
// handle failure
});
promise.finally(() => {
appElement.classList.remove("loading");
});
promise
.then(() => {
// handle success
})
.catch(() => {
// handle failure
})
.finally(() => {
appElement.classList.remove("loading");
});
promise
.then(() => {
// handle success
}, null)
.then(null, () => {
// handle failure
})
.then(() => {
appElement.classList.remove("loading");
}, () => {
appElement.classList.remove("loading");
})Метод Promise.resolve() принимает один аргумент и возвращает promise в состоянии fulfilled.
Метод Promise.reject() принимает один аргумент и возвращает promise в состоянии rejected.
const promise = Promise.resolve(42);
promise.then(value => {
console.log(value); // 42
});const promise = Promise.reject(42);
promise.catch(reason => {
console.log(reason); // 42
});Каждый вызов then(), catch() или finally() фактически создает и возвращает другой promise. Другой promise выполнится только после того, как предыдущий выполнен или отклонен.
const promise = Promise.resolve(42);
promise.then(value => {
console.log(value);
}).then(() => {
console.log("Finished");
});const promise1 = Promise.resolve(42);
const promise2 = promise1.then(value => {
console.log(value);
});
const promise3 = promise2.then(() => {
console.log("Finished");
});const promise = new Promise((resolve, reject) => {
console.log("Executor");
resolve(42);
});
promise.then(result => {
console.log(result);
});
/*
promise
.then(result => {
console.log(result);
})
.then(result => {
console.log(result);
});
*/
console.log("Hi!");// https://www.jsv9000.app/
const promise = new Promise((resolve, reject) => {
resolve(42);
});
const promise2 = new Promise((resolve, reject) => {
resolve(999);
});
promise
.then(function call1(){
console.log(1)
})
.then(function call2(){
console.log(2)
});
promise2
.then(function call3(){
console.log(3)
})
.then(function call4(){
console.log(4)
});const promise1 = new Promise((resolve, reject) => {
resolve(42);
});
const promise2 = promise1.then(value => {
console.log(value);
});
const promise3 = promise2.then(value => {
console.log(value);
});
/*
const promise2 = promise1.then(value => {
console.log(value);
return value+1
});
const promise3 = promise2.then(value => {
console.log(value);
});
*/// const promise = Promise.resolve(42);
const promise1 = new Promise((resolve, reject) => {
resolve(42);
});
const promise2 = promise1.then(value => {
console.log(value);
return Promise.resolve(value+1);
//return value+1
});
const promise3 = promise2.then(value => {
console.log(value);
});
promise3.catch((error) => console.log(error))const promise1 = new Promise((resolve, reject) => {
resolve(42);
});
promise2 = promise1.then(value => {
console.log(value);
return Promise.reject(value+1);
});
const promise3 = promise2.then(value => {
console.log(value);
});
promise3.catch((error) => console.log(error))const promise = new Promise((resolve, reject) => {
resolve(42);
});
promise1.then(value => {
return Promise.reject(value+1);
}).then(value => {
console.log(value);
}).catch((error) => console.log(error));fetch(`https://swapi.dev/api/films/${id}/`)
.then(function (response) {
// этот then сработает, когда разрешится промис с запросом данных о фильме
return response.json() // нужно распарсить ответ сервера, это асинхронная операция
})
.then(function (movie) {
// этот then сработает, когда данные о фильме распарсятся
const characterUrl = movie.characters[0]
return fetch(characterUrl) // вызов fetch вернет промис, возвращаем его из колбэка, чтобы продолжить цепочку
})
.then(function (response) {
// этот then сработает, когда разрешится промис с результатами запроса персонажа
return response.json()
})
.then(function (character) {
renderCharacterProfile(character)
})
.catch(function (err) {
// catch сработает, когда любая из операций выше завершится ошибкой
renderErrorMessage(err)
})Цепочки then() при обработке промисов могут быть очень большими.
catch() обрабатывает ошибки от всех then() между ним и предыдущим catch().
Если в цепочке несколько catch(), то каждый ловит ошибки от then(), находящихся выше.
fetch(`https://swapi.dev/api/films/${id}/`)
.then(function (response) {
})
.then(function (movie) {
})
.catch(function (err) {
})
.then(function (response) {
})
.then(function (character) {
})
.catch(function (err) {
})Возможен вариант, когда финального catch() нет. Тогда ошибки от последних then() не будут обрабатываться.
const promise = new Promise((resolve, reject) => {
throw new Error("Uh oh!");
});
promise.catch(reason => {
console.log(reason.message);
});const promise = new Promise((resolve, reject) => {
try {
throw new Error("Uh oh!");
} catch (ex) {
reject(ex);
}
});
promise.catch(reason => {
console.log(reason.message);
});const promise = new Promise((resolve, reject) => {
throw new Error("Uh oh!");
});
promise.catch(reason => {
console.log(reason.message); // "Uh oh!"
throw new Error("Oops!");
}).catch(reason => {
console.error(reason.message); // "Oops!"
});const promise = fetch("http://jsonplaceholder.typicode.com/posts");
promise.then(response => {
if (response.ok) {
console.log(response.status);
} else {
throw new Error(`Unexpected status code: ${response.status} ${response.statu\ sText}`);
}
}).catch(reason => {
console.error(reason.message);
});Всегда указывайте обработчик ошибок в конце цепочки обещаний, чтобы гарантировать, что вы сможете правильно обрабатывать любые ошибки, которые могут возникнуть.
Метод finally() ведет себя иначе, чем then() или catch(), поскольку он копирует состояние и значение предыдущего промиса в возвращаемый промис.
const promise1 = Promise.resolve(42);
const promise2 = promise1.finally(() => {
console.log("Finally called.");
});
promise2.then(value => {
console.log(value); // 42
});
promise1.finally(() => {
console.log("Finally called.");
}).then(value => {
console.log(value); // 42
});const promise = Promise.reject(43);
promise.finally(() => {
console.log("Finally called.");
}).catch(reason => {
console.error(reason); // 43
});const promise = Promise.resolve(42);
promise.finally(() => {
// settlement handler
return 43; // ignored!
}).then(value => {
// fulfillment handler
console.log(value); // 42
});const promise1 = Promise.reject(43);
promise1.finally(() => {
throw 44;
}).catch(reason => {
console.error(reason); // 44
});
const promise2 = Promise.reject(43);
promise2.finally(() => {
return Promise.reject(44);
}).catch(reason => {
console.error(reason); // 44
});
const appElement = document.getElementById("app");
const promise = fetch("books.json");
appElement.classList.add("loading");
promise.then(response => {
if (response.ok) {
console.log(response.status);
} else {
throw new Error('Unexpected status code: ${response.status} ${response.statu\ sText}');
}
}).finally(() => {
appElement.classList.remove("loading");
}).catch(reason => {
console.error(reason.message);
});
promise.then(response => {
}).catch((reason) => {
}).finally(() => {
});Возврат примитивных значений из обработчиков промисов позволяет передавать данные между промисами, но что, если вы возвращаете объект? Если объект является обещанием, то предпринимается дополнительный шаг, чтобы определить, как действовать дальше.
const promise1 = Promise.resolve(42);
const promise2 = Promise.resolve(43);
promise1.then(value => {
console.log(value); // 42
return promise2;
}).then(value => {
console.log(value); // 43
});const promise1 = Promise.resolve(42);
const promise2 = Promise.resolve(43);
const promise3 = promise1.then(value => {
console.log(value); // 42
return promise2;
});
promise3.then(value => {
console.log(value); // 43
});const promise1 = Promise.resolve(42);
const promise2 = Promise.reject(43);
promise1.then(value => {
console.log(value); // 42
return promise2;
}).then(value => {
console.log(value); // never called
});const promise1 = fetch("books.json");
promise1.then(response => {
const promise2 = response.json();
promise2.then(payload => {
console.log(payload);
}).catch(reason => {
console.error(reason.message);
});
}).catch(reason => {
console.error(reason.message);
});const promise = fetch("books.json");
promise.then(response => {
return response.json();
}).then(payload => {
console.log(payload);
}).catch(reason => {
console.error(reason.message);
});const p1 = Promise.resolve(42);
p1.then(value => {
console.log(value); // 42
// create a new promise
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(43);
}, 10000);
});
return p2;
}).then(value => {
console.log(value); // 43
});Метод all() используют, когда нужно запустить несколько промисов параллельно и дождаться их выполнения.
Если передать пустой массив, то Promise.all() будет выполнен немедленно.
const promise1 = new Promise(resolve => setTimeout(() => resolve(1), 5000))
const promise2 = new Promise(resolve => setTimeout(() => resolve(2), 2000))
const promise3 = new Promise(resolve => setTimeout(() => resolve(3), 1000))
Promise.all([promise1, promise2, promise3])
.then(([response1, response2, response3]) => {
console.log(response1)
// 1
console.log(response2)
// 2
console.log(response3)
// 3
}).catch(error => {
console.error(error)
// error
})
Promise.all([promise1, promise2, promise3])
.then((data) => {
console.log(data[0])
// 1
console.log(data[1])
// 2
console.log(data[2])
// 3
}).catch(error => {
console.error(error)
// error
})Если хотя бы один промис из переданного массива завершится с ошибкой, то Promise.all() тоже завершится с этой ошибкой. Метод уже не будет следить за выполнением оставшихся промисов, которые рано или поздно все-таки выполнятся, и их результаты будут просто проигнорированы.
const peopleIds = [1, 13, 3]
const arrayFetchUsers = peopleIds.map(user => fetch(`https://swapi.dev/api/people/${user}`).then((response) => response.json()))
Promise.all(arrayFetchUsers)
.then((responses) => {
// responses — массив результатов выполнения промисов
responses.forEach(resp => {
console.log(resp.name)
})
})
.catch(error => {
console.error(error)
})Promise.allSettled() очень похож на метод Promise.all(), но работает немного по-другому. В отличие от Promise.all(), Promise.allSettled() ждёт выполнения всех промисов, при этом неважно, завершились они успешно или с ошибкой.
const promises = [
new Promise(resolve => setTimeout(() => resolve(1), 3000)),
new Promise((resolve, reject) => setTimeout(() => reject('error'), 2000)),
new Promise(resolve => setTimeout(() => resolve(3), 1000))
]
Promise.allSettled(promises)
.then(([response1, response2, response3]) => {
console.log(response1)
// { status: 'fulfilled', value: 3 }
console.log(response2)
// { status: 'rejected', reason: 'error' }
console.log(response3)
// { status: 'fulfilled', value: 1 }
})
const urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.org/i_need_an_error',
]
const arrayFetchData = urls.map(url => fetch(url).then(res => res.json()))
Promise.allSettled(arrayFetchData)
.then((res) => {
// res — массив результатов выполнения промисов
res.forEach(item => {
console.log(item)
})
})Его используют, когда нужно запустить несколько промисов параллельно и дождаться первого успешного разрешённого.
Его используют, чтобы запустить несколько промисов и дождаться того, который выполнится быстрее.
Добавленное перед определением функции ключевое слово async делает функцию асинхронной. Возвращаемое значение такой функции автоматически оборачивается в Promise:
async function getStarWarsMovies() {
return 1
}
console.log(getStarWarsMovies()) // Promise { <state>: "fulfilled", <value>: 1 }async function getStarWarsMovie(id) {
const response = await fetch(`https://swapi.dev/api/films/${id}/`)
console.log("ответ получен", response)
return response.json()
}
const movies = getStarWarsMovie(1).then((movie) => {
console.log(movie.title)
})Соо-о это
function getMainActorProfileFromMovie(id) {
return fetch(`https://swapi.dev/api/films/${id}/`)
.then((movieResponse) => {
return movieResponse.json()
})
.then((movie) => {
const characterUrl = movie.characters[0].split("//")[1]
return fetch(`https://${characterUrl}`)
})
.then((characterResponse) => {
return characterResponse.json()
})
.catch((err) => {
console.error("Произошла ошибка!", err)
})
}
getMainActorProfileFromMovie(1).then((profile) => {
console.log(profile)
})переписывается на такой код
async function getMainActorProfileFromMovie(id) {
try {
const movieResponse = await fetch(`https://swapi.dev/api/films/${id}/`);
const movie = await movieResponse.json();
const characterUrl = movie.characters[0].split('//')[1];
const characterResponse = await fetch(`https://${characterUrl}`);
return characterResponse.json();
} catch (err) {
console.error('Произошла ошибка!', err);
}
}
getMainActorProfileFromMovie(1).then((profile) => {console.log(profile)});Ключевое слово await работает только внутри асинхронных функций. Вызов его вне функции будет синтаксической ошибкой
Всегда используйте async/await вместо цепочек then() и колбэков.
function addAsync (num1, num2, callback) {
// use the famous jQuery getJSON callback API
return $.getJSON('http://www.example.com', {
num1: num1,
num2: num2
}, callback);
}
addAsync(1, 2, success => {
resultA = success; // you get result = 3 here
addAsync(resultA, 3, success => {
resultB = success; // you get result = 6 here
addAsync(resultB, 4, success => {
resultC = success; // you get result = 10 here
addAsync(success, 4, success => {
addAsync(success, 10, success => {
addAsync(success, 10, success => {
});
});
});
});
});
});let resultA, resultB, resultC;
function addAsync(num1, num2) {
return fetch(`https://api.mathjs.org/v4/?expr=${num1}%2B${num2}`)
.then(x => x.text());
}
addAsync(1, 2)
.then(success => {
resultA = success;
return resultA;
})
.then(success => addAsync(success, 3))
.then(success => {
resultB = success;
return resultB;
})
.then(success => addAsync(success, 4))
.then(success => {
resultC = success;
return resultC;
})
.then(success => {
console.log('total: ' + success)
console.log(resultA, resultB, resultC)
});const addAsync = async (num1, num2) => {
const response = await fetch(`https://api.mathjs.org/v4/?expr=${num1}%2B${num2}`)
return await response.text()
}
const resultA = await addAsync(1, 2);
console.log(1);
const resultB = await addAsync(resultA, 3);
console.log(2);
const resultC = await addAsync(resultB, 4);
console.log(3);
console.log(resultA, resultB, resultC);https://learn.javascript.ru/network
https://learn.javascript.ru/xmlhttprequest
XMLHttpRequest – это встроенный в браузер объект, который даёт возможность делать HTTP-запросы к серверу без перезагрузки страницы.
const xhr = new XMLHttpRequest();
// xhr.onload
xhr.addEventListener("load", () => {
console.log(xhr.status);
console.log(xhr.response);
});
xhr.addEventListener("error", error => {
console.log(error);
});
xhr.open("get", url);
xhr.responseType = 'json';
xhr.send();function request(url, callback){
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", () => {
callback(xhr.response)
});
xhr.addEventListener("error", error => {
console.log(error);
});
xhr.open("get", url);
xhr.responseType = 'json';
xhr.send();
}
request('https://jsonplaceholder.typicode.com/users/2', (response) => {
const userId = response.id
request(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`, (response) => {
console.log(response)
})
})function requestURL(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", () => {
resolve(xhr.response);
});
xhr.addEventListener("error", error => {
reject(error);
});
xhr.open("get", url);
xhr.responseType = 'json';
xhr.send();
});
}
requestURL('https://jsonplaceholder.typicode.com/users/2')
.then((response) => {
const userId = response.id
return requestURL(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
})
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error)
})async function requestURL(url)
{
return await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", () => {
resolve(xhr.response);
});
xhr.addEventListener("error", error => {
reject(error);
});
xhr.open("get", url);
xhr.responseType = 'json';
xhr.send();
});
}
const responseUser = await requestURL('https://jsonplaceholder.typicode.com/users/2');
const responsePost = await requestURL(`https://jsonplaceholder.typicode.com/posts?userId=${responseUser.id}`);
console.log(responsePost);
https://learn.javascript.ru/fetch
Браузер предоставляет глобальный API для работы с запросами и ответами HTTP. Раньше для подобной работы использовался XMLHttpRequest, однако fetch() более гибкая и мощная альтернатива, он понятнее и проще в использовании из-за того, что использует Promise.
Функция fetch() принимает два параметра:
- url — адрес, по которому нужно сделать запрос;
- options (необязательный) — объект конфигурации, в котором можно настроить метод запроса, тело запроса, заголовки и многое другое.
По умолчанию вызов fetch() делает GET-запрос по указанному адресу. Базовый вызов для получения данных можно записать таким образом:
fetch('http://jsonplaceholder.typicode.com/posts')Результатом вызова fetch() будет Promise, в котором будет содержаться специальный объект ответа Response.
- ok — принимает состояние true или false и сообщает об успешности запроса;
- json() — метод, вызов которого, возвращает результат запроса в виде json.
- text() — метод, вызов которого, возвращает результат запроса в виде текста.
- blob() - метод, вызов которого, возвращает результат запроса в виде двоичных данных.
const newPost = {
title: 'foo',
body: 'bar',
userId: 1,
}
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST', // Здесь так же могут быть GET, PUT, DELETE
body: JSON.stringify(newPost), // Тело запроса в JSON-формате
headers: {
// Добавляем необходимые заголовки
'Content-type': 'application/json; charset=UTF-8',
},
})
.then((response) => response.json())
.then((data) => {
console.log(data)
// {title: "foo", body: "bar", userId: 1, id: 101}
})По умолчанию fetch() запросы не включают в себя cookies и потому авторизованные запросы на сервере могут не пройти. Для этого необходимо добавить в настройку поле credentials:
fetch('https://somesite.com/admin', {
method: 'GET',
// или 'same-origin' если можно делать такие запросы только в пределах этого домена
credentials: 'include',
})Любой ответ на запрос через fetch() (например HTTP-код 400, 404 или 500) переводит Promise в состояние fulfilled. Промис перейдёт в состояние rejected только если запрос не случился из-за сбоя сети или что-то помешало выполнению fetch().
// Запрос вернет ошибку 404 Not Found
fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route').catch(
() => {
console.log('Error occurred!')
}
) // Никогда не выполнится
fetch('https://jsonplaceholder.typicode.com/there-is-no-such-route')
.then((response) => {
// Проверяем успешность запроса и выкидываем ошибку
if (!response.ok) {
throw new Error('Error occurred!')
}
return response.json()
})
// Теперь попадём сюда, т.к выбросили ошибку
.catch((err) => {
console.log(err)
}) // Error: Error occurred!установить расширение
https://chrome.google.com/webstore/detail/jsonvue/chklaanhfefbnpoihckbnefhakgolnmc
API:
https://techcrunch.com/wp-json/wp/v2/posts
https://techcrunch.com/wp-json/wp/v2/categories
https://techcrunch.com/wp-json/wp/v2/posts?categories=577030456
https://techcrunch.com/wp-json/wp/v2/posts?page=3
https://techcrunch.com/wp-json/wp/v2/posts?categories=577030456&page=2
вывести посты через css гриды
добавить select в котором будет список категорий
при выборе категории, фильтровать посты
добавить пагинацию, простую, next - prev
доп. задача
в hash добавить номер страницы и фильтр категорий