Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active May 2, 2025 11:01
Show Gist options
  • Save sunmeat/c084ca34fa415f49fb6b7bff11351aba to your computer and use it in GitHub Desktop.
Save sunmeat/c084ca34fa415f49fb6b7bff11351aba to your computer and use it in GitHub Desktop.
async await вместо промисов
// код на промисах: https://gist.github.com/sunmeat/404f684da54c7e1b6fe5d4957e410bd6 356 строк вместо 407
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<title>async await вместо промисов</title>
<style>
body {
font-family: 'Courier New', Courier, monospace;
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
color: #00ffe5;
padding: 3rem;
line-height: 1.6;
}
h1, h2 {
text-align: center;
color: #ff0077;
text-shadow: 2px 2px 5px #000;
}
section {
background: rgba(0, 0, 0, 0.7);
border: 1px solid #00ffe5;
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
box-shadow: 0 0 20px #00ffe5;
}
pre {
background: #111;
color: #0ff;
padding: 1rem;
border-radius: 10px;
overflow-x: auto;
}
code {
font-size: 1rem;
}
button {
background-color: #ff0077;
border: none;
padding: 0.8rem 2rem;
color: white;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
transition: 0.3s;
display: block;
margin: 1rem auto 0;
}
button:hover {
background-color: #00ffe5;
color: #111;
}
input[type="number"] {
background: #111;
color: #0ff;
border: 1px solid #00ffe5;
padding: 0.5rem;
border-radius: 8px;
font-size: 1rem;
width: 100px;
margin: 0 auto;
display: block;
}
label {
display: block;
text-align: center;
margin-bottom: 0.5rem;
}
img {
display: block;
max-width: 100%;
height: auto;
margin: 1rem auto;
border: 2px solid #00ffe5;
border-radius: 10px;
box-shadow: 0 0 15px #00ffe5;
}
</style>
</head>
<body>
<h1>Комбиниция АПИ на async await</h1>
<section>
<h2>Найти картину по году</h2>
<label for="yearInput">Введите год создания картины:</label>
<input type="number" id="yearInput" placeholder="Год" min="1000" max="2025" value="1989">
<img id="paintingImage" src="" alt="Картина" style="display: none;">
<pre><code id="result"></code></pre>
<button onclick="findPainting()">Найти картину</button>
</section>
<script>
// получение случайной картины по году (Harvard Art Museums API)
async function getPaintingByYear(year) {
const apiKey = '3a0ac081-0b02-4fa4-8b3d-aebc991047aa';
const url = `https://api.harvardartmuseums.org/object?yearmade=${year}&hasimage=1&size=100&apikey=${apiKey}`;
const response = await fetch(url);
if (!response.ok) throw new Error('Ошибка при запросе к Harvard API');
const data = await response.json();
if (!data.records || data.records.length === 0) {
throw new Error('Картины за этот год не найдены');
}
return data.records;
}
// получение информации об авторе картины
async function getArtistInfo(artistId) {
const apiKey = '3a0ac081-0b02-4fa4-8b3d-aebc991047aa';
const url = `https://api.harvardartmuseums.org/person/${artistId}?apikey=${apiKey}`;
const response = await fetch(url);
if (!response.ok) throw new Error('Ошибка при запросе информации об авторе');
return await response.json();
}
// геокодирование города рождения художника (Nominatim)
async function getCityCoordinates(city) {
const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(city)}&format=json&limit=1&addressdetails=1`;
const response = await fetch(url, {
headers: { 'User-Agent': 'PaintingSunsetApp/1.0' }
});
if (!response.ok) throw new Error('Ошибка при геокодировании города');
const data = await response.json();
if (!data || data.length === 0) {
throw new Error('Город не найден');
}
return {
lat: parseFloat(data[0].lat),
lon: parseFloat(data[0].lon),
display_name: data[0].display_name,
timezone: data[0].timezone || null
};
}
// поиск ближайшего магазина АТБ
async function findNearestATB(lat, lon) {
const url = `https://nominatim.openstreetmap.org/search?q=АТБ+near+${lat},${lon}&format=json&limit=1&addressdetails=1`;
const response = await fetch(url, {
headers: { 'User-Agent': 'PaintingSunsetApp/1.0' }
});
if (!response.ok) throw new Error('Ошибка при поиске магазина АТБ');
const data = await response.json();
if (!data || data.length === 0) {
return 'Магазин АТБ не найден поблизости';
}
const store = data[0];
const storeLat = parseFloat(store.lat);
const storeLon = parseFloat(store.lon);
const distance = haversineDistance(lat, lon, storeLat, storeLon);
return `Ближайший магазин АТБ: ${store.display_name}\nКоординаты: (${storeLat}, ${storeLon})\nРасстояние: ${distance.toFixed(2)} км`;
}
// рассчёт расстояния между двумя точками (формула гаверсинуса)
function haversineDistance(lat1, lon1, lat2, lon2) {
const R = 6371; // Радиус Земли в км
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
// повторные попытки для запросов
async function fetchWithRetry(url, options, retries = 3, delayMs = 1000) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
return await response.json();
} catch (error) {
if (i < retries - 1) {
await new Promise(resolve => setTimeout(resolve, delayMs));
} else {
throw error;
}
}
}
}
// получение текущего времени и часового пояса
async function getCurrentTime(lat, lon) {
const formatTime = (date, timezone) => date.toLocaleTimeString('ru-RU', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: timezone
});
try {
const apiKey = '1AKVDXKKF55B';
const url = `http://api.timezonedb.com/v2.1/get-time-zone?key=${apiKey}&format=json&by=position&lat=${lat}&lng=${lon}`;
const data = await fetchWithRetry(url, { headers: { 'User-Agent': 'PaintingSunsetApp/1.0' } });
if (!data.status || data.status !== 'OK' || !data.timestamp || !data.zoneName) {
throw new Error('Данные о времени или часовом поясе недоступны');
}
console.log('TimeZoneDB response:', data);
const date = new Date(data.timestamp * 1000);
const time = formatTime(date, data.zoneName);
const isDst = data.dst === 1;
const dstOffsetHours = isDst ? 1 : 0;
return {
currentTime: time,
timezone: data.zoneName,
dst_offset: dstOffsetHours,
is_dst: isDst
};
} catch (error) {
console.error('TimeZoneDB error:', error);
try {
const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lon}&date=today`;
const data = await fetchWithRetry(url, { headers: { 'User-Agent': 'PaintingSunsetApp/1.0' } });
if (data.status !== 'OK' || !data.results.timezone) {
throw new Error('Ошибка в данных Sunrise-Sunset API');
}
console.log('Sunrise-Sunset response:', data);
const timezone = data.results.timezone || 'UTC';
const date = new Date();
const time = formatTime(date, timezone);
const isDst = timezone.includes('America') && new Date().getMonth() === 3;
const dstOffsetHours = isDst ? 1 : 0;
return {
currentTime: time,
timezone: timezone,
dst_offset: dstOffsetHours,
is_dst: isDst
};
} catch (fallbackError) {
console.error('Sunrise-Sunset error:', fallbackError);
return {
currentTime: 'Ошибка: не удалось получить время',
timezone: 'Недоступно',
dst_offset: 0,
is_dst: false
};
}
}
}
// получение времени восхода/заката и длительности дня
async function getSunriseSunset(lat, lon, timezone) {
const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lon}&date=today${timezone && timezone !== 'UTC' ? `&tzid=${encodeURIComponent(timezone)}` : ''}`;
try {
const data = await fetchWithRetry(url, { headers: { 'User-Agent': 'PaintingSunsetApp/1.0' } });
if (data.status !== 'OK') {
throw new Error('Ошибка в данных Sunrise-Sunset API');
}
return {
sunrise: data.results.sunrise,
sunset: data.results.sunset,
day_length: data.results.day_length
};
} catch (error) {
console.error('Sunrise-Sunset API error:', error);
return {
sunrise: 'Недоступно',
sunset: 'Недоступно',
day_length: 'Недоступно'
};
}
}
// задержка для предотвращения превышения лимитов API
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// основная функция для обработки запроса
async function findPainting() {
const year = document.getElementById('yearInput').value;
const output = document.getElementById('result');
const image = document.getElementById('paintingImage');
output.textContent = 'Ищем картину...\n';
image.style.display = 'none';
image.src = '';
if (!year || isNaN(year) || year < 1000 || year > 2025) {
output.textContent = 'Ошибка: Введите корректный год (1000-2025)';
return;
}
try {
const paintings = await getPaintingByYear(year);
let painting, artist, birthplace;
let attempts = 0;
const maxAttempts = paintings.length > 10 ? 10 : paintings.length;
while (attempts < maxAttempts) {
const randomIndex = Math.floor(Math.random() * paintings.length);
painting = paintings[randomIndex];
attempts++;
if (!painting.people || painting.people.length === 0) continue;
const artistId = painting.people[0].personid;
artist = await getArtistInfo(artistId);
birthplace = artist.birthplace || 'Неизвестно';
if (artist.displayname && birthplace !== 'Неизвестно') break;
}
if (attempts >= maxAttempts) {
throw new Error('Не удалось найти картину с известным автором и городом рождения');
}
output.textContent += `Картина: ${painting.title || 'Без названия'}\n`;
if (painting.primaryimageurl) {
image.src = painting.primaryimageurl;
image.style.display = 'block';
} else {
output.textContent += 'Изображение картины недоступно\n';
}
output.textContent += `Художник: ${artist.displayname}\n`;
output.textContent += `Город рождения: ${birthplace}\n`;
const city = await getCityCoordinates(birthplace);
output.textContent += `Координаты города: (${city.lat}, ${city.lon})\n`;
output.textContent += `Полное название: ${city.display_name}\n`;
await delay(1000);
const timeData = await getCurrentTime(city.lat, city.lon);
output.textContent += `Текущее время: ${timeData.currentTime} (${timeData.timezone})\n`;
const sunData = await getSunriseSunset(city.lat, city.lon, timeData.timezone);
output.textContent += `Восход: ${sunData.sunrise}\n`;
output.textContent += `Закат: ${sunData.sunset}\n`;
output.textContent += `Длительность дня: ${sunData.day_length}\n`;
const atbInfo = await findNearestATB(city.lat, city.lon);
output.textContent += `${atbInfo}\n`;
output.textContent += 'Задача выполнена!';
} catch (error) {
console.error('Ошибка:', error);
output.textContent += `Ошибка: ${error.message || 'Не удалось выполнить запрос. Попробуйте снова.'}`;
}
}
// обработчик нажатия Enter в поле ввода
document.getElementById('yearInput').addEventListener('keypress', function(event) {
if (event.key === 'Enter') {
findPainting();
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment