📜 ⬆️ ⬇️

We master async / await with a real example

The async / await design is a relatively new approach to writing asynchronous code in JavaScript. It is based on promises and, as a result, does not block the main thread. The innovation of this design lies in the fact that thanks to it, the asynchronous code becomes similar to synchronous and behaves in this way. This opens up great opportunities for the programmer.

image

Before the emergence of async / await, callbacks and promises were used in the development of asynchronous program mechanisms. The author of the material, the translation of which we publish today, suggests first to remember how to write code in the old way, and then, using a real example, to study the use of async / await.

Callbacks


Here is how the code example uses callbacks (callback functions):

setTimeout(() => {  console.log('This runs after 1000 milliseconds.'); }, 1000); 

When using nested callbacks, there is a problem called callback hell. Here is a simplified code example to illustrate this problem:

 asyncCallOne(() => { asyncCallTwo(() => {   asyncCallThree(() => {     asyncCallFour(() => {       asyncCallFive(() => {         //выполнение неких действий       })     })   }) }) }) 

If there are structures in the code that consist of callbacks nested in each other, such code may be difficult to understand, it will be difficult to maintain.

Promises


Here is an example of code that uses promises (Promise objects):

 const promiseFunction = new Promise((resolve, reject) => { const add = (a, b) => a + b; resolve(add(2, 2)); }); promiseFunction.then((response) => { console.log(response); }).catch((error) => { console.log(error); }); 

The promiseFunction() function of this example returns a Promise object, which is some kind of action performed by the function. A call to the resolve() method indicates that its work has been successfully completed. In similar constructions, the .then() promis method is also used - with its help, after successfully resolving the promise, you can perform a kind of callback. The .catch() method is called in cases when something went wrong during the work of the promise.

Asynchronous functions


Functions declared using the async (asynchronous functions) enable us to write neat and not overloaded with service structures code that allows us to get the same result that we obtained using promises. It should be noted that the async is, in essence, only “syntactic sugar” for promis.

Asynchronous functions are created using the async keyword when declaring a function. It looks like this:

 const asyncFunction = async () => { // Код } 

The execution of the asynchronous function can be suspended using the await keyword. It can only be used in asynchronous functions. It allows you to return the result of the asynchronous function, which will be available after such a function completes the execution of a certain task.

Let's compare the work of the asynchronous function and promise, which return the string:

 // Асинхронная функция const asyncGreeting = async () => 'Greetings'; // Промис const promiseGreeting = () => new Promise(((resolve) => { resolve('Greetings'); })); asyncGreeting().then(result => console.log(result)); promiseGreeting().then(result => console.log(result)); 

It is easy to see that using the async allows you to write asynchronous code that looks like synchronous. This code is much easier to work with.

Now that we have covered the basic things, let's move on to our example.

Currency Converter


▍Preliminary training


Here we will create a simple but informative application from the point of view of studying the async / await construction. It is a currency converter that uses real data from the corresponding APIs. The program accepts the amount in a certain currency, the code of this currency, as well as the code of the currency into which we want to convert this amount. After that, the program displays the result, after downloading the current data on exchange rates. The program also displays a list of countries in which you can spend money in the currency in which the transfer of a given amount.

In particular, here we are going to use data from two asynchronous sources of information:

  1. Service currencylayer.com . On this site you will need to create a free account and get a key to access the API (API Access Key). From here we will take the data necessary to convert the amount from one currency to another.
  2. Service restcountries.eu . It can be used without registration. From here we will upload data about where you can use the currency in which we converted the specified amount of money.

Create a new directory and execute the npm init command in it. When the program asks us a question about the name of the package being created, enter currency-converter . The remaining questions of the program can not be answered by pressing Enter in response. After that, install the Axios package in our project by running the command npm install axios --save in its directory. Create a new file named currency-converter.js .

Let's start writing the program code by connecting Axios in this file:

 const axios = require('axios'); 

In our project there will be three asynchronous functions. The first will upload currency data. The second will load country data. The third will collect this data, present it in a user-friendly form and display it on the screen.

▍The first function - asynchronous loading of currency data


Create an asynchronous function getExchangeRate() , which will take two arguments: fromCurrency and toCurrency :

 const getExchangeRate = async (fromCurrency, toCurrency) => {} 

In this function, we need to load the data. Thanks to the use of the async / await construction, it is possible to write the resulting data directly to some variable or constant. Before writing the code of this function, do not forget to register on the site and get the API access key. To load the data we need the following construction:

 const response = await axios.get('http://www.apilayer.net/api/live?access_key=[ваш код доступа к API]'); 

After receiving the system response, we will be able to find the data we need in the response object at the address response.data.quotes . Here is what a fragment of an object looks like with data of interest to us (it is visible in the program as response.data ):

 {  "success":true,  "terms":"https:\/\/currencylayer.com\/terms",  "privacy":"https:\/\/currencylayer.com\/privacy",  "timestamp":1547891348,  "source":"USD",  "quotes":{     "USDAED":3.673042,     "USDAFN":75.350404,     "USDALL":109.203989, ...     "USDZWL":322.355011  } } 

Place the object with currency rates in the rate constant:

 const rate = response.data.quotes; 

The base currency code can be found at response.data.source . Write the base currency code in the baseCurrency constant:

 const baseCurrency = response.data.source; 

Since, by default, the data returned by this API is the currency rate in relation to the US dollar (USD), we create a constant usd into which we write the result of dividing 1 by the currency rate in which the amount is given:

 const usd = 1 / rate[`${baseCurrency}${fromCurrency}`]; 

Pay attention to how the key is formed, according to which we get the value of the course. In the object obtained from the API, a fragment of which is given above, the keys are lines starting with USD and ending with the code of the corresponding currency. Since it is assumed that our program accepts string currency codes, we generate a key by concatenating a string containing the code of the base currency and what is passed to the function in the fromCurrency parameter.

Now, in order to get the exchange rate fromCurrency to the currency toCurrence , we multiply the constant usd by the rate for toCurrency . It looks like this:

 const exchangeRate = usd * rate[`${baseCurrency}${toCurrency}`]; 

As a result, what gets into exchangeRate returned from the function. Here is what its full code looks like:

 const getExchangeRate = async (fromCurrency, toCurrency) => {   try {     const response = await axios.get('http://www.apilayer.net/api/live?access_key=[ваш код доступа к API]');     const rate = response.data.quotes;     const baseCurrency = response.data.source;     const usd = 1 / rate[`${baseCurrency}${fromCurrency}`];     const exchangeRate = usd * rate[`${baseCurrency}${toCurrency}`];     return exchangeRate;   } catch (error) {     throw new Error(`Unable to get currency ${fromCurrency} and ${toCurrency}`);   } }; 

Note that to handle errors that may occur during the execution of a query, the usual try / catch construct is used.

▍Second function - asynchronous download of country data


Our second function, getCountries() , which asynchronously loads information about countries in which you can use the currency into which we convert the amount specified in another currency, will take the argument currencyCode :

 const getCountries = async (currencyCode) => {} 

To load data, we use the following command:

 const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`); 

If, for example, we use the HRK (Croatian Kuna) code in the request, then in response we will receive a JSON-code, a fragment of which is shown below:

 [   {     "name":"Croatia", ...  } ] 

It is an array of objects with information about countries. The properties of the name these objects contain the name of the country. You can access this array using the response.data construction. Apply the map() array method to extract the names of countries from the received data and return the data from the getCountries() function, which will be an array of names of countries:

 return response.data.map(country => country.name); 

Here is the full code of the getCountries() function:

 const getCountries = async (currencyCode) => {   try {     const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);     return response.data.map(country => country.name);   } catch (error) {     throw new Error(`Unable to get countries that use ${currencyCode}`);   } }; 

▍Third function - data collection and output


Our third asynchronous function, convertCurrency () , will take arguments fromCurrency , toCurrency and amount - currency codes and an amount.

 const convertCurrency = async (fromCurrency, toCurrency, amount) => {} 

In it, we first get the exchange rate:

 const exchangeRate = await getExchangeRate(fromCurrency, toCurrency); 

Then we load the list of countries:

 const countries = await getCountries(toCurrency); 

Next - perform the conversion:

 const convertedAmount = (amount * exchangeRate).toFixed(2); 

And after all the necessary data has been collected, we return the string that the user of the program sees:

 return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`; 

Here is the complete function code:

 const convertCurrency = async (fromCurrency, toCurrency, amount) => {   const exchangeRate = await getExchangeRate(fromCurrency, toCurrency);   const countries = await getCountries(toCurrency);   const convertedAmount = (amount * exchangeRate).toFixed(2);   return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`; }; 

Notice that this function does not have a try / catch block, since it only works with the results provided to it by the two previously described functions.

▍Start the program


We prepared three functions, two of which load data from various services, and one collects this data and prepares for output. Now we just have to call this function, passing it all the necessary. We will not implement mechanisms here that allow us to call our program from the command line with the transfer of currency codes and amounts to it, although you can easily do this if you wish. We simply call the convertCurrency() function, passing it the necessary data:

 convertCurrency('EUR', 'HRK', 20)   .then((message) => {     console.log(message);   }).catch((error) => {     console.log(error.message);   }); 

Here we want to find out - for how much Croatian kunas you can exchange 20 euros and in the process find out in which countries you can spend this money.

Call the program by entering the following command in the terminal:

 node currency-converter.js 

In response, we get about the following.


The result of the program

Here, just in case, the complete code of our project.

 const axios = require('axios'); const getExchangeRate = async (fromCurrency, toCurrency) => {   try {     const response = await axios.get('http://www.apilayer.net/api/live?access_key=[ваш код доступа к API]');     const rate = response.data.quotes;     const baseCurrency = response.data.source;     const usd = 1 / rate[`${baseCurrency}${fromCurrency}`];     const exchangeRate = usd * rate[`${baseCurrency}${toCurrency}`];     return exchangeRate;   } catch (error) {     throw new Error(`Unable to get currency ${fromCurrency} and ${toCurrency}`);   } }; const getCountries = async (currencyCode) => {   try {     const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);     return response.data.map(country => country.name);   } catch (error) {     throw new Error(`Unable to get countries that use ${currencyCode}`);   } }; const convertCurrency = async (fromCurrency, toCurrency, amount) => {   const exchangeRate = await getExchangeRate(fromCurrency, toCurrency);   const countries = await getCountries(toCurrency);   const convertedAmount = (amount * exchangeRate).toFixed(2);   return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`; }; convertCurrency('EUR', 'HRK', 20)   .then((message) => {     console.log(message);   }).catch((error) => {     console.log(error.message);   }); 

Results


We hope that this example of using the async / await construction in real conditions has helped those who did not understand this construction before understand it.

Dear readers! If you use the async / await construction in practice, please share your impressions about it.

Source: https://habr.com/ru/post/436884/