Master Asynchronous JavaScript with these 3 Coding Challenges to Elevate Your Skills
JavaScript, as one of the most popular programming languages, offers a vast range of functionalities. Among these, asynchronous JavaScript stands out for its ability to handle tasks that may take some time to complete, such as fetching data from a server or loading images. In this article, we will explore three engaging coding challenges that will help you master asynchronous JavaScript. These challenges include geocoding with GPS coordinates, creating an image loader, and utilizing async/await for cleaner asynchronous code. Let’s dive in!
Challenge 1: Where Am I? Using Geolocation and Geocoding APIs
Objective
Build a function whereAmI
that takes GPS coordinates (latitude and longitude) and uses the Geocode.xyz API to reverse geocode these coordinates, converting them into a meaningful location (city and country). Then, render the country data using the RestCountries API.
Solution
First, we create a function whereAmI
which takes latitude and longitude as inputs. Using the fetch
API, we make an AJAX call to Geocode.xyz to get the location data.
const whereAmI = function (lat, lng) {
fetch(`https://geocode.xyz/${lat},${lng}?geoit=json`)
.then(response => {
if (!response.ok) throw new Error(`Problem with geocoding (${response.status})`);
return response.json();
})
.then(data => {
console.log(`You are in ${data.city}, ${data.country}`);
return fetch(`https://restcountries.com/v3.1/name/${data.country}`);
})
.then(response => {
if (!response.ok) throw new Error(`Problem with country data (${response.status})`);
return response.json();
})
.then(data => renderCountry(data[0]))
.catch(err => console.error(`${err.message} 💥`));
};
const renderCountry = function (data) {
const html = `
<article class="country">
<img class="country__img" src="${data.flags.png}" />
<div class="country__data">
<h3 class="country__name">${data.name.common}</h3>
<h4 class="country__region">${data.region}</h4>
<p class="country__row"><span>👫</span>${(+data.population / 1000000).toFixed(1)} million people</p>
<p class="country__row"><span>🗣️</span>${Object.values(data.languages).join(', ')}</p>
<p class="country__row"><span>💰</span>${Object.values(data.currencies).map(cur => cur.name).join(', ')}</p>
</div>
</article>`;
countriesContainer.insertAdjacentHTML('beforeend', html);
countriesContainer.style.opacity = 1;
};
whereAmI(52.508, 13.381);
whereAmI(19.037, 72.873);
whereAmI(-33.933, 18.474);
This function first fetches the geocoding data, logs the city and country to the console, and then fetches and renders the country data.
Challenge 2: Image Loading with Promises
Objective
Create a function createImage
that loads an image from a given path and appends it to the DOM. Implement functionality to load multiple images sequentially with a delay between each image.
Solution
First, we create a function createImage
that returns a promise. This promise resolves when the image is loaded and rejects if there is an error.
const imageContainer = document.querySelector('.images');
const wait = function (seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
};
const createImage = function (imgPath) {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.src = imgPath;
img.addEventListener('load', () => {
imageContainer.append(img);
resolve(img);
});
img.addEventListener('error', () => {
reject(new Error('Image not found'));
});
});
};
createImage('img/img-1.jpg')
.then(img => {
console.log('Image 1 loaded');
return wait(2);
})
.then(() => {
document.querySelector('img').style.display = 'none';
return createImage('img/img-2.jpg');
})
.then(img => {
console.log('Image 2 loaded');
return wait(2);
})
.then(() => {
document.querySelector('img').style.display = 'none';
return createImage('img/img-3.jpg');
})
.then(img => {
console.log('Image 3 loaded');
})
.catch(err => console.error(err));
This code loads images one by one, with a 2-second pause between each image, and hides the previous image when loading the next one.
Challenge 3: Async/Await for Cleaner Asynchronous Code
Objective
Recreate Challenge 2 using async/await to handle promises. Additionally, create a function loadAll
to load multiple images in parallel.
Solution
First, we refactor the previous challenge using async/await.
const loadNPause = async function () {
try {
let img = await createImage('img/img-1.jpg');
console.log('Image 1 loaded');
await wait(2);
img.style.display = 'none';
img = await createImage('img/img-2.jpg');
console.log('Image 2 loaded');
await wait(2);
img.style.display = 'none';
img = await createImage('img/img-3.jpg');
console.log('Image 3 loaded');
} catch (err) {
console.error(err);
}
};
loadNPause();
Next, we create the loadAll
function to load images in parallel.
const loadAll = async function (imgArr) {
try {
const imgs = imgArr.map(async img => await createImage(img));
const imgEls = await Promise.all(imgs);
imgEls.forEach(img => img.classList.add('parallel'));
} catch (err) {
console.error(err);
}
};
loadAll(['img/img-1.jpg', 'img/img-2.jpg', 'img/img-3.jpg']);
This function uses Promise.all
to load all images simultaneously and applies a CSS class to each loaded image.
Conclusion
Asynchronous JavaScript can seem daunting at first, but with practice, it becomes a powerful tool in your programming arsenal. These three challenges provided practical experience with APIs, promises, and async/await. Whether you are fetching geolocation data, loading images sequentially or in parallel, mastering these concepts will enhance your ability to build dynamic and responsive web applications.