Master Asynchronous JavaScript with these 3 Coding Challenges to Elevate Your Skills

Ebo Jackson
3 min readAug 5, 2024

--

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.

--

--