JavaScript: semplificare e riutilizzare i risultati delle Promise con il modello async/await

Short link

Una caratteristica delle funzioni asincrone in JavaScript è che possono essere utilizzate per semplificare il risultato di una Promise.

Possiamo infatti utilizzare il costrutto return per restituire il risultato di una funzione asincrona o di una routine in base allo stato della Promise.


'use strict';

const evenOddFunc = () => {
    let randInt = Math.round((Math.random() * 10) + 1);
    return new Promise( (resolve, reject) => {
        if(randInt % 2 === 0) {
            resolve(randInt);
        } else {
            reject(0);
        }
    });
};


const testFuncAsync = async () => {
    try {
        let result = await evenOddFunc();
        return true;
    } catch(err) {
        return false;
    }
};

(async function() {
    for(let i = 0; i < 10; i++) {
        let isEvenOdd = await testFuncAsync();
        console.log(isEvenOdd);
    }
})();

In questo caso la funzione asincrona restituisce un valore booleano come risoluzione della sua Promise. Notate che a noi non interessa il numero restituito dalla prima Promise quando è nello stato resolved ma solo sapere se il numero generato è pari o dispari. Nel blocco try sappiamo che una Promise è risolta, mentre nel blocco catch accade il contrario. Quindi nel primo caso restituiremo true mentre nel secondo false.

In pratica stiamo semplificando una Promise ad un valore più semplice che può essere utilizzato altrove. Immaginiamo di dover testare la configurazione della posta elettronica per un dominio in Node.js. Per prima cosa, dato un indirizzo e-mail, dobbiamo ottenere l'elenco dei record MX del dominio effettuando una query DNS.


'use strict';

const getMXRecords = email => {
    const dns = require('dns');
    return new Promise((resolve, reject) => {
        let domain = email.split('@')[1];
        dns.resolve(domain, 'MX', (err, addresses) => {
            if(err) {
                reject(err);
            } else {
                resolve(addresses);
            }
        });
    });
};

A questo punto abbiamo un array di oggetti che rappresentano i record MX. La Promise si limita a restituire tale array senza effettuare ulteriori verifiche. Le verifiche, che consistono nel appurare se i record MX contengono la stringa corretta del sottodominio MX scelto, spettano ad una funzione asincrona.


'use strict';

const isValidMXConf = async (email, subdodmain) => {
      try {
          let records = await getMXRecords(email);
          let found = false;

          if(Array.isArray(records) && records.length > 0) {
              records.forEach(record => {
                 if(record.exchange && typeof record.exchange === 'string') {
                     if(record.exchange.includes(subdomain)) {
                         found = true;
                     }
                 }
              });
          }
          return found;
      } catch(err) {
          return false;
      }
};

La funzione asincrona semplifica e verifica i risultati ottenuti dalla prima funzione. In questo modo possiamo riutilizzare tale valore altrove.


'use strict';

const testMXConf = async (email, subdomain) => {
    try {
       let result = await isValidMXConf(email, subdodmain);
       console.log( email + ': ' + result );
    }  catch(err) {
        console.log(err);
    }
};

Esempio:


'use strict';

testMXConf('info@gabrieleromanato.com', 'google.com'); // true
testMXConf('gabriele@gabrieleromanato.name', 'google.com'); // false