NodeJS async/await

A partire da ECMAScript 2017 e consolidato in ECMAScript 2018 nasce il costrutto async/await in javascript portato in nodeJS nella versione 8 ma cosa è questo costrutto di cui tanto si sente parlare?

In sostanza il costrutto non è altro che, come spesso si dice, “zucchero sintattico” ad indicare che è un modo diverso di scrivere la stessa cosa utilizzando costrutti diversi.

pensiamo ad un costrutto “while” ed al suo modo di scriverlo in modo diverso utilizzando un costrutto “For” ma utilizzando la stessa logica; meno codice, più pulizia ma, sotto sotto, è sempre un ciclo “while”.

Ebbene questo è il costrutto async/await di NodeJS, un modo diverso di scrivere una promise di cui abbiamo parlato in questo precedente articolo (link).

Come ricorderemo una promise è scritta più o meno in questo modo…

/* promise nodeJS */
let valore =5
const prom=new Promise((res,rej)=>{
     if (valore ==5){
         res("Valore esatto")
     }else{
        rej("Valore errato")
    }
})
/*Utilizzo*/
function utilizzo() {
    prom.then((data)=>{
        console.log(data)
}).catch((data)=>{
        console.err(data)
})
}
utilizzo()

Bene, la stessa cosa possiamo scriverla in maniera più concisa utilizzando una funzione async e scrivendo semplicemente del codice che ha l’aspetto di codice sincrono ma che, ovviamente (siamo in NodeJS) lavora in maniera asincrona.

Questo tipo di costrutto elimina le sezioni then e catch, per cui la gestione delle eccezioni va effettuata attraverso un costrutto try…catch.

Vediamo il seguente codice facendo attenzione a come è stata modificata la funzione ‘utilizzo’ e come è stata richiamata…

/* NodeJS costrutto async/await */
const prom=new Promise((res,rej)=>{
    let valore =5
    if (valore ==5){
        res("Valore esatto")
    }else{
       rej("Valore errato")
   }
})
/*Utilizzo*/
async function utilizzo() {
   return await prom
}
utilizzo().then((data)=>{console.log(data)}).catch((data)=>console.log("Errore :"+data))

Ora la funzione utilizzo è di tipo ‘async’ e prevede una ‘await’ che attende la risoluzione della promise prima di ritornare (è sempre tutto asincrono). il ‘then’ ed il catch sono stati eliminati (vengono effettuati in automatico dal costrutto) ed il codice risulta più pulito.

Ad una prima impressione potrebbe apparire tutto molto uguale ma considerate che la funzione ‘utilizzo’ potrebbe attendere la terminazione di più di una promise ed in questo modo si evitano le catene di ‘then’ e ‘catch’. Guardate quest’altro esempio…

/* promise nodeJS */
function controllaValore(val) {
    let prom = new Promise((res, rej) => {
        if (val > 5) {
            res("Valore esatto")
        } else {
            rej("Valore errato")
        }
    })
    return prom
}
/*Utilizzo*/
function utilizzo() {
    controllaValore(6).then((data) => {
        console.log(data)
        controllaValore(7).then((data) => {
            console.log(data)
            controllaValore(8).then((data) => {
                console.log(data)
            }).catch((data) => {
                console.log("Errore : " + data)
            })
        }).catch((data) => {
            console.log("Errore : " + data)
        })
    }).catch((data) => {
        console.log("Errore : " + data)
    })
}
utilizzo()

In questo esempio c’è la funzione ‘controllaValore’ che ritorna una promise e viene utilizzata per verificare che il valore passato in ‘val’ sia maggiore di 5. All’interno della funzione utilizzo, la funzione controlla valore è chiamata 3 volte creando una serie di blocchi ‘then’ e ‘catch’ (che sono definiti come ‘promise hell’); utilizzando async/await il tutto si riduce in maniera più elegente e con l’impressione di una funzione sincrona…

/*Utilizzo di async/await in NodeJS*/
function controllaValore(val) {
    let prom = new Promise((res, rej) => {
        if (val > 5) {
            res("Valore esatto")
        } else {
            rej("Valore errato")
        }
    })
    return prom
}
/*Utilizzo*/
async function utilizzo() {
    try {
        await controllaValore(6)
        await controllaValore(7)
        await controllaValore(8)
        return "Tutti i valori sono esatti"
    } catch {
        return "c'è un valore non esatto"
    }
}
utilizzo().then((data) => { console.log(data) }).catch((data) => console.log("Errore :" + data))

La funzione utilizzo attende le varie chiamate a ‘controllaValore’ e si comporta allo stesso modo dell’esempio precendente ma in maniera più concisa ed elegante. (Vero che abbiamo l’impressione che ‘utilizzo’ abbia una struttura sincrona?).

Se una sola delle attese di ‘controllaValore’ viene rigettata (reject / rej) allora viene eseguito il blocco catch. Notare che ora che non esiste ‘then’ e ‘catch’ le eccezioni sono da gestire attraverso try…catch.

Provate ad attendere controllaValore specificando un valore minore o uguale a 5 e vedrete cosa succede.

Alcune cose da dire sul costrutto async/await sono le seguenti:

  • await è possibile utilizzarlo soltanto in funzioni di tipo async
  • come per le promise non è possibile utilizzarle nel corpo principale del processo (a meno di non creare una funzione async anonima)
  • una funzione async ritorna sempre una promise (ecco perchè possiamo utilizzare l’ultima riga utilizzando i normali ‘then’ e ‘catch’).

Per quanto abbia pensato di parlare poco del costrutto async/await, l’articolo è venuto ‘lungo’ e tante altre cose sarebbero da dire ma, come dico sempre, non è lo scopo di questo spazio.

Cercate, provate, studiate e vedrete che riuscirete facilmente a padroneggiare le promise (che non è proprio un argomento semplice).

Alla prossima e… buona programmazione a tutti.

Data di ultima modifica 29 Gennaio 2024