Attendi che una funzione finisca in JavaScript

Moataz Farid 12 ottobre 2023
  1. Sync e Async in JavaScript
  2. Usa callback per aspettare che una funzione finisca in JavaScript
  3. Usa le promises per aspettare che una funzione finisca in JavaScript
  4. Usa async/await per attendere che una funzione finisca prima di continuare l’esecuzione
Attendi che una funzione finisca in JavaScript

Questo tutorial introdurrà JavaScript Callbacks, Promises e Async/await e ti mostrerà come attendere il termine di una funzione asincrona prima di continuare l’esecuzione.

Per capire cosa sono Promises e Async/await, dobbiamo prima capire quali sono le funzioni Sync e Async in JavaScript.

Sync e Async in JavaScript

La programmazione sincrona esegue un comando alla volta. Quando chiamiamo una funzione che esegue un’azione di lunga durata, interromperà il programma fino al termine.

JavaScript è tradizionalmente a thread singolo, anche con multi-core. Possiamo far sì che esegua attività solo su un singolo thread chiamato thread principale.

Tale comportamento sincrono è un fattore limitante nei multi-thread ma aiuta l’utente a scrivere codici senza preoccuparsi dei problemi di concorrenza.

Durante la programmazione asincrona, l’azione a esecuzione prolungata verrà eseguita in un altro thread diverso dal thread principale, quindi l’esecuzione principale non viene bloccata. Quando quella funzione di lunga durata termina, il programma principale viene informato e ottiene l’accesso ai risultati.

La maggior parte delle primitive di I/O in JavaScript non sono bloccanti. Le richieste di rete, le operazioni sui file system sono tutte operazioni non bloccanti. Il blocco è l’eccezione in JavaScript.

Poiché JavaScript è basato su tecniche di programmazione asincrona, ci sono più approcci come callbacks, promises e async/await che ti consentono di mettere in sequenza le esecuzioni delle tue funzioni. In modo che un blocco di codice o una funzione non vengano eseguiti prima del termine di un’altra funzione specifica.

sincronizzazione e asincrono

La figura sopra mostra la netta variazione tra l’esecuzione asincrona e sincrona di due funzioni.

Usa callback per aspettare che una funzione finisca in JavaScript

Se abbiamo istruzioni sincrone, eseguire queste istruzioni l’una dopo l’altra è semplice.

function one() {
  console.log('I am function One');
}
function Two() {
  console.log('I am function Two');
}

one();
Two();

Produzione:

I am function One 
I am function Two

Supponiamo di voler eseguire due funzioni, functionOne() e functionTwo() in modo tale che functionOne() debba essere eseguita dopo aver eseguito alcune istruzioni asincrone all’interno di functionTwo().

function functionOne(_callback) {
  // do some asynchronus work
  _callback();
}

function functionTwo() {
  // do some asynchronus work
  functionOne(() => {
    console.log('I am a callback');
  });
}

functionTwo();

Quando si esegue il codice di cui sopra, l’ultima cosa stampata nella console è I am a callback. Il famoso esempio di callback è la funzione setTimeout con una funzione handler da eseguire dopo il timeout.

function testCallBack() {
  console.log('log before use setTimeout function');
  setTimeout(() => {
    console.log('inside timeout');
  }, 5000);
  console.log('log after use setTimeout function');
}

testCallBack();

Produzione:

log before use setTimeout function
log after use setTimeout function
inside timeout

Usa le promises per aspettare che una funzione finisca in JavaScript

Una Promises è un oggetto che rappresenta l’eventuale adempimento o fallimento di un’operazione asincrona. Alleghiamo il callback di adempimento alla Promises con una o più istruzioni then, e when può chiamare il callback del gestore degli errori nel catch.

doFirstThing()
    .then(result => doSecondThing(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => {
      console.log('The final result thing' + finalResult);
    })
    .catch(failureCallbackfunction);
}

Incatenare le dichiarazioni then e catch come nell’esempio precedente è uno dei vantaggi delle promesse. Promettiamo di fare doSecondThing(result) una volta che doFirstThing() è soddisfatto. Gli argomenti del then sono opzionali, ma obbligatori se devi restituire un risultato.

In caso di errore, il browser cercherà il catch alla fine della catena e lo eseguirà. È molto simile al famoso try-catch.

Il seguente esempio ci aiuterà a capire le catene delle promises e ci mostrerà come possiamo aspettare che una funzione con comportamento asincrono termini l’esecuzione prima di poter continuare l’esecuzione.

var resolvedFlag = true;

let mypromise = function functionOne(testInput) {
  console.log('Entered function');
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Inside the promise');
      if (resolvedFlag == true) {
        resolve('Resolved');
      } else {
        reject('Rejected')
      }
    }, 2000);
  });
};

mypromise()
    .then((res) => {console.log(`The function recieved with value ${res}`)})
    .catch((error) => {
      console.log(`Handling error as we received ${error}`);
    });

Produzione:

Entered function
Inside the promise
The function received with value Resolved

Creare una promessa può essere facile come restituire una nuova Promise(). Il costruttore Promise() riceve una funzione come argomento, che dovrebbe avere due parametri: resolve e reject.

Nel caso in cui il nostro evento sia soddisfatto e abbiamo bisogno di restituire il risultato, usiamo la funzione resolve() quando riusciamo in quello che stavamo facendo. Ma se si verifica un errore e deve essere gestito, usiamo reject() per inviare l’errore al catch.

Impostiamo il flag resolvedFlag = true per simulare la gestione degli errori nel catch. Se resolvedFlag è impostato su false, viene chiamata la funzione reject() e l’errore viene gestito nel blocco catch.

Produzione:

Entered function
Inside the promise
Handling error as we received Rejected

Usa async/await per attendere che una funzione finisca prima di continuare l’esecuzione

Un altro modo per attendere l’esecuzione di una funzione prima di continuare l’esecuzione nell’ambiente asincrono in JavaScript è usare async/wait.

La funzione async è la funzione dichiarata dalla parola chiave async, mentre all’interno della funzione async è consentita solo la parola chiave await e utilizzata per sospendere l’avanzamento all’interno della funzione async fino all’operazione asincrona basata sulla promessa è soddisfatto o rifiutato.

Le parole chiave async e await consentono un comportamento asincrono basato sulle promesse in uno stile più pulito.

Capiamo come funziona async/await. La funzione che stiamo aspettando dovrebbe restituire un’istanza della classe Promise per attendere che venga eseguita utilizzando la parola chiave await prima di chiamarla. Come accennato in precedenza, la funzione che contiene l’istruzione await deve essere dichiarata con l’istruzione async.

L’esempio seguente mostra come attendere che la funzione basata sulla promessa finisca prima di continuare l’esecuzione.

function testAsync() {
  return new Promise((resolve, reject) => {
    // here our function should be implemented
    setTimeout(() => {
      console.log('Hello from inside the testAsync function');
      resolve();
      ;
    }, 5000);
  });
}

async function callerFun() {
  console.log('Caller');
  await testAsync();
  console.log('After waiting');
}

callerFun();

Produzione:

Caller
Hello from inside the testAsync function
After waiting