Smetti di forzare async/await
Le promesse sono davvero di tendenza.
Dal principio dei tempi, gli sviluppatori hanno combattuto molte lotte inutili. Dal classico “Tabs vs. Spaces” al senza tempo dibattito “Mac vs. PC”, siamo bravi a trovare argomenti di distrazione.
Risposte: Linux & Spaces.
La lotta…?
Promises vs. Async/Await!
Aspetta, è una lotta? Deve esserlo, vero? Non ne parliamo più di callback?
No,non è una lotta. In definitiva è semplicemente un altro possibile strumento nel tuo arsenale. Tuttavia, poiché async/await non sostituisce tutte le funzionalità di Promise (in particolare Promise.all, .race) presentarlo come un sostituto è fuorviante.
Molte persone influenti promuovono questo equivoco: async/await è il sostituto delle Promise che tutti hanno aspettato per.
Suggerimento: No, assolutamente no, e neanche per poco.
Una recente aggiunta a VS Code alimenta questo bias. Come ha twittato @umaar:
Visual Studio Code can now convert your long chains of Promise.then()‘s into async/await! 🎊 Works very well in both JavaScript and TypeScript files. .catch() is also correctly converted to try/catch ✅ pic.x.com/xb39Lsp84V
— Umar Hansa (@umaar) September 28, 2018
Se odi le Promise e vuoi questa funzionalità di refactoring, non ti biasimo.
Empatizzo. Capisco.
Ci sono passato. 🤗
Usavo odiare le Promise. Oggi sono tornato indietro completamente. Le Promise sono fantastiche. Ti consentono/incentivano a sfruttare la composizione di funzioni.
Ci sono 2 aree su cui consiglio di concentrarsi per prima cosa per migliorare la tua tecnica con le Promise.
#1: Funzioni con nome!
Elimina i metodi anonimi. L’uso di funzioni con nome fa sì che il codice legga come poesia dei tuoi requisiti.
Diamo un’occhiata a un esempio comune:
Eseguire una richiesta HTTP GET usando fetch:
Anti‑Pattern
// ❌ Using anonymous inline functions 💩fetch(url) .then(response => response.status < 400 ? response : Promise.reject(new Error('Request Failed: ' + response.ststus))) .then(response => response.text())Soluzione: Metodi con nome
// ✅ Clarity emerges: named functionsfetch(url) .then(checkResponse) .then(getText)
// Reusable general-purpose functionsfunction checkResponse(response) { return response.status < 400 ? response : Promise.reject(new Error('Request Failed: ' + response.ststus))}function getText(response) { return response.text()}I vantaggi di questo approccio diventano sempre più evidenti man mano che il codice diventa più DRY.
Risorse aggiuntive: Dai un’occhiata ai miei video da 1 minuto su log di base e debug avanzato usando questa tecnica.
#2: Scopo Unico (Funzioni)
Suona sorprendentemente preciso: Scopo Unico.
Eppure è così soggettivo, arbitrario e, a volte, persino privo di senso.
// 1 punto: il return & ternario sono effettivamente una riga unicafunction checkResponse(response) { return response.status < 400 ? response : Promise.reject(new Error('Request Failed: ' + response.ststus))}// 1 punto: il return & l'espressione sono anch'essi una riga unicafunction getText(response) { return response.text()}Dato il codice di una funzione, aggiungi 1 punto per ogni riga che contiene uno dei seguenti token: if, return, ternario, for, const, let, var, switch, while, [].map/filter/reduce/etc. Aggiungi 1 punto per ogni istruzione (ignora le righe vuote). Una catena di espressioni o metodi conta solo come 1 punto.
Uff, un po’ di gergo.
Interessante, la maggior parte degli sviluppatori afferma di essere abbastanza bravi a mantenere il Singolo Scopo del loro codice. Non è un caso isolato: dicono anche di essere ottimi guidatori!
Diamo un’occhiata a un esempio presentato dal (incredibilmente talentuoso) Jake Archibald nel suo articolo su async/await per il sito Google Developers (nota: 2024, link rimosso).
// source: https://developers.google.com/web/fundamentals/primers/async-functionsfunction logInOrder(urls) { // fetch all the URLs const textPromises = urls.map(url => { return fetch(url).then(response => response.text()); });
// log them in order textPromises.reduce((chain, textPromise) => { return chain.then(() => textPromise) .then(text => console.log(text)); }, Promise.resolve());}Scopo unico?
Direi di no. Cosa fa logInOrder?
- itera su una lista di
urls - le passa a una chiamata HTTP GET inline:
fetchHTTP- restituisce il corpo della risposta come testo
- aggiunge un
.then(text => console.log(text))dopo ogni promessa intextPromise- stampa i risultati in serie
Ci sono 5 metodi anonimi definiti in questa singola funzione. Come osserva Jake, il .reduce è troppo complesso. Non ha senso scrivere a mano meccanismi sfumati in tutto il codice. In altre parole, non scriviamo codice di creazione DOM con infinite chiamate a document.createElement(), element.setAttribute(), ecc. Invece scegliamo lo strumento migliore tra le varie opzioni: funzioni di supporto/utility, librerie o framework.
Soluzione: Funzioni a Scopo Unico
Inizia estrapolando i metodi…

Prosegui sostituendo il .reduce() e logPromise() con un Promise.all e un ..map()…

Riepilogo
Prova ad applicare queste tecniche al tuo codice! Poi mandami un tweet e fammi sapere come è andata. Oppure, se hai domande o commenti, contattami pure!
Aiuta a diffondere #PromiseTruth e condividi questo articolo. ❤️
