我们掌握一个异步/等待一个真实的例子

async / await构造是在JavaScript中编写异步代码的一种相对较新的方法。 它基于承诺,因此不会阻塞主流。 这种设计的创新之处在于,异步代码变得类似于同步代码,并且行为类似。 这为程序员带来了巨大的机会。

图片

在异步/等待出现之前,在异步程序机制的开发中使用了回调和Promise。 该材料的作者(我们今天将其翻译发表)建议您首先回顾如何以旧的方式编写代码,然后使用一个真实的示例研究async / await的用法。

回呼


以下是使用回调(回调函数)的示例代码:

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

使用嵌套的回调函数时,存在一个称为回调地狱的问题。 这是一个简化的代码示例来说明此问题:

 asyncCallOne(() => { asyncCallTwo(() => {   asyncCallThree(() => {     asyncCallFour(() => {       asyncCallFive(() => {         //         })     })   }) }) }) 

如果代码包含由彼此嵌套的回调组成的结构,则这样的代码可能很难理解,将很难维护。

承诺


这是使用promise(Promise对象)的示例代码:

 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); }); 

此示例中的promiseFunction()函数返回Promise对象,这是该函数执行的特定操作。 调用resolve()方法表示可以成功完成操作。 在此类构造中,还使用了promise的.then()方法-在它的帮助下,成功解决了promise之后,您可以执行某个回调。 如果在.catch()的工作中出现问题,则将调用.catch()方法。

异步功能


使用async声明的函数(异步函数)使我们有机会编写简洁且不会因服务构造而过载的代码,这使我们可以获得与使用promise所获得的结果相同的结果。 应该注意的是, async本质上只是诺言的“语法糖”。

声明函数时,使用async关键字创建async函数。 看起来像这样:

 const asyncFunction = async () => { //  } 

可以使用await关键字暂停异步功能。 它只能用于异步功能。 它允许您返回异步函数的结果,该结果将在此类函数完成任务的执行后提供。

比较异步函数的操作和返回字符串的promise:

 //   const asyncGreeting = async () => 'Greetings'; //  const promiseGreeting = () => new Promise(((resolve) => { resolve('Greetings'); })); asyncGreeting().then(result => console.log(result)); promiseGreeting().then(result => console.log(result)); 

async ,使用async使您可以编写看起来像同步的异步代码。 此代码更容易使用。

既然我们已经介绍了基本的知识,那么让我们继续进行示例。

货币换算器


▍初步准备


在这里,我们将从研究异步/等待应用程序的构造的角度创建一个简单但认知的方法。 它是一种货币转换器,使用从相应API获得的真实数据。 程序接受以某种货币表示的金额,该货币的代码以及我们要将其转换成的货币代码。 之后,程序首先通过下载汇率上的当前数据来显示结果。 该程序还会显示一个国家/地区列表,您可以在其中以指定金额的货币兑换货币。

特别是在这里,我们将使用来自两个异步信息源的数据:

  1. Currencylayer.com服务。 在此站点上,您将需要创建一个免费帐户并获取用于访问API的密钥(API访问密钥)。 从这里我们将获取必要的数据,以将金额从一种货币转换为另一种货币。
  2. 服务restcountries.eu 。 无需注册即可使用。 在这里,我们上传有关您可以在何处使用将指定金额转换成的货币的数据。

创建一个新目录并在其中npm init命令。 当程序向我们询问有关要创建的软件包名称的问题时,我们将介绍currency-converter 。 您不能通过按Enter来回答程序的其他问题。 之后,通过在目录中运行npm install axios --save命令,将Axios软件包安装到我们的项目中。 创建一个名为currency-converter.js的新文件。

让我们开始通过在此文件中连接Axios来编写程序代码:

 const axios = require('axios'); 

我们的项目将具有三个异步功能。 第一个将加载货币数据。 第二个将加载国家/地区数据。 第三者将收集这些数据,以用户友好的形式呈现并在屏幕上显示。

▍第一个功能是异步加载货币数据


让我们创建一个异步getExchangeRate()函数,该函数将接受两个参数: fromCurrencytoCurrency

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

在此功能中,我们需要加载数据。 通过使用async / await构造,您可以将接收到的数据直接写到某个变量或常量。 在编写该功能的代码之前,请不要忘记在网站上注册并获得API访问密钥。 要加载数据,我们需要以下构造:

 const response = await axios.get('http://www.apilayer.net/api/live?access_key=[    API]'); 

收到系统响应后,可以在response对象中的response处找到我们需要的数据。 这是对象片段,其中包含我们感兴趣的数据(在程序中显示为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  } } 

将带有汇率的对象放在rate常数中:

 const rate = response.data.quotes; 

基本货币代码可以在response.data.source找到。 我们以常量baseCurrency编写基础货币的代码:

 const baseCurrency = response.data.source; 

由于默认情况下,此API返回的数据是对美元(USD)的汇率,因此创建一个常量usd ,我们将写入除以指定金额的汇率的结果除以1所得的结果:

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

注意密钥的形成方式,并据此获得课程的价值。 在从API获取的对象中(上面已给出了其片段),键是以USD开头并以相应货币的代码结尾的字符串。 由于假定我们的程序接受字符串形式的货币代码,因此我们通过串联包含基本货币代码的字符串以及在fromCurrency参数中传递给函数的字符串来生成密钥。

现在,为了获得从fromCurrencytoCurrence的汇率,我们将常数usd乘以toCurrency 。 看起来像这样:

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

结果,我们将返回进入exchangeRate 。 其完整代码如下所示:

 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}`);   } }; 

请注意,常规的try / catch构造用于处理查询执行期间可能发生的错误。

▍第二个功能是异步加载国家/地区数据


我们的第二个函数getCountries()异步地加载有关您可以在其中使用货币的国家/地区的信息,在该货币中,我们会将另一种货币指定的金额转换为该货币,该函数将采用currencyCode参数:

 const getCountries = async (currencyCode) => {} 

要加载数据,我们使用以下命令:

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

例如,如果我们在请求中使用HRK代码(克罗地亚库纳),则我们将收到一个JSON代码作为响应,其片段如下所示:

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

它是包含有关国家/地区信息的一组对象。 这些对象的name属性包含国家/地区的名称。 您可以使用response.data构造访问此数组。 我们应用map()数组方法从接收到的数据中提取国家/地区的名称,然后从getCountries()函数返回此数据,该函数将是国家/地区名称的数组:

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

这是完整的getCountries()函数代码:

 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}`);   } }; 

function第三功能-数据收集与输出


我们的第三个异步函数convertCurrency ()将接受参数fromCurrencytoCurrencytoCurrency货币代码和数量。

 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}`; 

这是完整的功能代码:

 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}`; }; 

请注意,此功能没有try / catch块,因为它仅适用于上述两个功能提供的结果。

▍启动程序


我们已经准备了三个功能,其中两个从各种服务加载数据,一个收集这些数据并准备输出。 现在,我们只需要调用此函数,即可将所需的所有信息传递给它。 尽管此处可以根据需要执行此操作,但是我们不会在此处实现允许通过命令行将货币和总和代码转移到命令行来调用程序的机制。 我们只是调用convertCurrency()函数,并向其传递必要的数据:

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

在这里,我们想找出多少克罗地亚克纳可以兑换20欧元,并一路找出这笔钱可以在哪些国家/地区使用。

我们通过在终端中输入以下命令来调用该程序:

 node currency-converter.js 

作为回应,我们得到以下内容。


程序的结果

为了以防万一,这里是我们项目的完整代码。

 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);   }); 

总结


我们希望这个在实际条件下使用async / await构造的示例可以帮助那些以前不了解此构造的人弄清楚该构造。

亲爱的读者们! 如果您在实践中使用async / await构造,请分享您对此的印象。

Source: https://habr.com/ru/post/zh-CN436884/


All Articles