JavaScript asincrono: spiegazione di callback e promesse

Gestire il codice asincrono, ovvero il codice che non viene eseguito immediatamente come richieste web o timer, può essere complicato. JavaScript ci offre due modi fuori dagli schemi per gestire il comportamento asincrono: callback e promesse.

I callback erano l'unico modo supportato in modo nativo per gestire il codice asincrono fino al 2016, quando l' Promiseoggetto è stato introdotto nel linguaggio. Tuttavia, gli sviluppatori JavaScript avevano implementato funzionalità simili da anni prima che le promesse arrivassero sulla scena. Diamo un'occhiata ad alcune delle differenze tra callback e promesse e vediamo come affrontiamo il coordinamento di più promesse.

Le funzioni asincrone che utilizzano i callback accettano una funzione come parametro, che verrà chiamato una volta completato il lavoro. Se hai usato qualcosa di simile setTimeoutnel browser, hai usato i callback.

// Puoi definire la tua richiamata separatamente ...

lascia che myCallback = () => {

  console.log ('Chiamato!');

};

setTimeout (myCallback, 3000);

// ... ma è anche comune vedere i callback definiti in linea

setTimeout (() => {

  console.log ('Chiamato!');

}, 3000);

Di solito la funzione che accetta un callback lo prende come ultimo argomento. Questo non è il caso sopra, quindi facciamo finta che ci sia una nuova funzione chiamata waitche è proprio come setTimeoutma accetta i primi due argomenti in ordine opposto:

// Useremmo la nostra nuova funzione in questo modo:

waitCallback (3000, () => {

  console.log ('Chiamato!');

});

Callback annidati e la piramide del destino

I callback funzionano bene per la gestione del codice asincrono, ma diventano complicati quando inizi a dover coordinare più funzioni asincrone. Ad esempio, se volessimo aspettare due secondi e registrare qualcosa, quindi attendere tre secondi e registrare qualcos'altro, quindi attendere quattro secondi e registrare qualcos'altro, la nostra sintassi diventa profondamente nidificata.

// Useremmo la nostra nuova funzione in questo modo:

waitCallback (2000, () => {

  console.log ('First Callback!');

  waitCallback (3000, () => {

    console.log ('Seconda richiamata!');

    waitCallback (4000, () => {

      console.log ("Terza richiamata!");

    });

  });

});

Questo può sembrare un esempio banale (e lo è), ma non è raro effettuare più richieste web di seguito in base ai risultati di ritorno di una richiesta precedente. Se la tua libreria AJAX utilizza i callback, vedrai la struttura sopra riprodotta. Negli esempi che sono più profondamente nidificati, vedrai quella che viene definita la piramide del destino, che prende il nome dalla forma piramidale creata nello spazio bianco rientrato all'inizio delle linee.

Come puoi vedere, il nostro codice diventa strutturalmente alterato e più difficile da leggere quando si tratta di funzioni asincrone che devono avvenire in sequenza. Ma diventa ancora più complicato. Immagina di voler avviare tre o quattro richieste web ed eseguire alcune attività solo dopo che tutte sono state restituite. Ti incoraggio a provare a farlo se non hai affrontato la sfida prima.

Più facile asincrono con le promesse

Le promesse forniscono un'API più flessibile per gestire le attività asincrone. Richiede che la funzione sia scritta in modo tale da restituire un Promiseoggetto, che ha alcune caratteristiche standard per gestire il comportamento successivo e coordinare più promesse. Se la nostra waitCallbackfunzione fosse Promise-based, ci vorrebbe solo un argomento, ovvero i millisecondi di attesa. Qualsiasi funzionalità successiva verrebbe incatenata alla promessa. Il nostro primo esempio sarebbe simile a questo:

lascia che myHandler = () => {

  console.log ('Chiamato!');

};

waitPromise (3000) .then (myHandler);

Nell'esempio sopra, waitPromise(3000)restituisce un Promiseoggetto che dispone di alcuni metodi da utilizzare, come then. Se volessimo eseguire più funzioni asincrone una dopo l'altra, potremmo evitare la piramide del destino usando le promesse. Quel codice, riscritto per supportare la nostra nuova promessa, sarebbe simile a questo:

// Non importa quante attività asincrone sequenziali abbiamo, non creiamo mai la piramide.

waitPromise (2000)

  .then (() => {

    console.log ('First Callback!');

    return waitPromise (3000);

  })

  .then (() => {

    console.log ('Seconda richiamata!');

    return waitPromise (4000);

  })

  .then (() => {

    console.log ('Seconda richiamata!');

    return waitPromise (4000);

  });

Meglio ancora, se abbiamo bisogno di coordinare attività asincrone che supportano le promesse, potremmo usare all, che è un metodo statico Promisesull'oggetto che accetta diverse promesse e le combina in una sola. Sarebbe come:

Promise.all ([

  waitPromise (2000),

  waitPromise (3000),

  waitPromise (4000)

]). then (() => console.log ('Everything is done!'));

La prossima settimana approfondiremo come funzionano le promesse e come usarle in modo idiomatico. Se stai solo imparando JavaScript o sei interessato a testare le tue conoscenze, prova waitCallbacko prova a ottenere l'equivalente di Promise.allcon i callback.

Come sempre, contattami su Twitter con commenti o domande.