Promise & Async/Await

Gestisci operazioni asincrone con Promise, async/await e fetch API

⏰ Programmazione Asincrona

JavaScript è single-threaded (un solo thread), ma può gestire operazioni
asincrone come chiamate API, timer, lettura file senza bloccare l’esecuzione.
Le Promise e async/await rendono questo processo pulito e gestibile!

💡 Cos’è il Codice Asincrono?

Immagina di ordinare una pizza online:

  • 🍕 Sincrono: Aspetti davanti al forno finché è pronta (blocchi tutto)
  • Asincrono: Ordini e fai altro, ricevi notifica quando pronta (non blocchi)

JavaScript usa l’approccio asincrono per operazioni lunghe come fetch API, timer, ecc.

🎯 In Questa Lezione

  • Promise – Gestire operazioni asincrone
  • then/catch/finally – Gestire successo/errore
  • async/await – Sintassi moderna (⭐ CONSIGLIATA)
  • fetch API – Chiamate HTTP
  • Error Handling – Gestire errori
  • Promise.all/race – Promise multiple

😱 Il Problema: Callback Hell

Prima delle Promise, JavaScript usava solo callback per operazioni asincrone.
Questo portava al temuto “Callback Hell” o “Pyramid of Doom”!

Callback Hell (❌ Da Evitare)
// ❌ CALLBACK HELL - Difficile da leggere e mantenere
getUser(userId, (user) => {
    getOrders(user.id, (orders) => {
        getOrderDetails(orders[0].id, (details) => {
            getPaymentInfo(details.paymentId, (payment) => {
                processPayment(payment, (result) => {
                    console.log("Pagamento completato!");
                }, (error) => {
                    console.error("Errore pagamento:", error);
                });
            }, (error) => {
                console.error("Errore payment info:", error);
            });
        }, (error) => {
            console.error("Errore dettagli:", error);
        });
    }, (error) => {
        console.error("Errore ordini:", error);
    });
}, (error) => {
    console.error("Errore utente:", error);
});

// Problemi:
// 😱 Difficile da leggere (piramide)
// 😱 Gestione errori ripetitiva
// 😱 Difficile da debuggare
// 😱 Difficile aggiungere logica

💡 La Soluzione: Promise e Async/Await

Le Promise e async/await risolvono questi problemi rendendo il codice asincrono
più leggibile, gestibile e simile a codice sincrono!

🤝 Promise

Una Promise è un oggetto che rappresenta il completamento (o fallimento)
futuro di un’operazione asincrona.

1
Stati di una Promise

Una Promise può essere in uno di tre stati:

Stati Promise
// 1. PENDING (in attesa) - Stato iniziale
// 2. FULFILLED (risolta) - Operazione completata con successo
// 3. REJECTED (rifiutata) - Operazione fallita con errore

// ⚠️ Una volta risolta/rifiutata, lo stato è IMMUTABILE

2
Creare una Promise

Promise Constructor
// Sintassi: new Promise((resolve, reject) => { ... })

const myPromise = new Promise((resolve, reject) => {
    // Operazione asincrona
    const success = true;
    
    if (success) {
        resolve("Operazione riuscita!");  // Promise FULFILLED
    } else {
        reject("Operazione fallita!");    // Promise REJECTED
    }
});

// Esempio: Simula chiamata API
function fetchUserData(userId) {
    return new Promise((resolve, reject) => {
        // Simula delay rete
        setTimeout(() => {
            if (userId > 0) {
                const user = {
                    id: userId,
                    nome: "Mario",
                    email: "mario@example.com"
                };
                resolve(user);  // Successo
            } else {
                reject(new Error("User ID non valido"));  // Errore
            }
        }, 1000);  // 1 secondo delay
    });
}

// Esempio: Promise immediata
const promiseRisolta = Promise.resolve("Valore");
const promiseRifiutata = Promise.reject("Errore");

3
Consumare Promise: then/catch/finally

then/catch/finally
fetchUserData(1)
    .then((user) => {
        // Eseguito se Promise FULFILLED
        console.log("Utente:", user);
        return user.id;  // Passa al prossimo then
    })
    .then((userId) => {
        // Chain - prende return precedente
        console.log("User ID:", userId);
    })
    .catch((error) => {
        // Eseguito se Promise REJECTED
        console.error("Errore:", error);
    })
    .finally(() => {
        // Eseguito SEMPRE (successo o errore)
        console.log("Operazione completata");
    });

// Output (dopo 1 secondo):
// Utente: {id: 1, nome: "Mario", email: "mario@example.com"}
// User ID: 1
// Operazione completata

// Con errore
fetchUserData(-1)
    .then((user) => {
        console.log("Utente:", user);
    })
    .catch((error) => {
        console.error("Errore:", error.message);
    })
    .finally(() => {
        console.log("Fine");
    });

// Output (dopo 1 secondo):
// Errore: User ID non valido
// Fine

4
Promise Chaining

Concatena più operazioni asincrone in sequenza.

Promise Chain
// Ogni then ritorna una nuova Promise
function getUser(id) {
    return new Promise((resolve) => {
        setTimeout(() => resolve({ id, nome: "Mario" }), 500);
    });
}

function getOrders(userId) {
    return new Promise((resolve) => {
        setTimeout(() => resolve([
            { id: 1, total: 100 },
            { id: 2, total: 200 }
        ]), 500);
    });
}

function getOrderDetails(orderId) {
    return new Promise((resolve) => {
        setTimeout(() => resolve({
            id: orderId,
            items: ["Item 1", "Item 2"],
            total: 100
        }), 500);
    });
}

// ✅ Con Promise Chaining (meglio di callback hell!)
getUser(1)
    .then((user) => {
        console.log("User:", user.nome);
        return getOrders(user.id);  // Ritorna Promise
    })
    .then((orders) => {
        console.log("Orders:", orders.length);
        return getOrderDetails(orders[0].id);  // Ritorna Promise
    })
    .then((details) => {
        console.log("Dettagli:", details);
    })
    .catch((error) => {
        console.error("Errore:", error);
    });

// Output (dopo ~1.5 secondi):
// User: Mario
// Orders: 2
// Dettagli: {id: 1, items: [...], total: 100}

// Molto più leggibile del callback hell! ✅

⭐ Async/Await (ES2017)

async/await è syntactic sugar per Promise che rende il codice asincrono
sembrare sincrono. 🌟 QUESTO È IL METODO MODERNO CONSIGLIATO!

1
Sintassi async/await

async/await Base
// async - Dichiara funzione asincrona (ritorna sempre Promise)
async function myFunction() {
    return "Hello";  // Automaticamente wrapped in Promise
}

myFunction().then(result => console.log(result));  // "Hello"

// await - Aspetta risoluzione Promise (SOLO dentro async function!)
async function fetchData() {
    const promise = new Promise((resolve) => {
        setTimeout(() => resolve("Dati caricati"), 1000);
    });
    
    const result = await promise;  // Aspetta 1 secondo
    console.log(result);
}

fetchData();  // Output dopo 1 secondo: "Dati caricati"

// ⚠️ await SOLO in async function!
// await somePromise;  // ERRORE se fuori async function

2
Confronto then vs async/await

Confronto
// ❌ Con then (vecchio modo)
function fetchUserThen(id) {
    return getUser(id)
        .then((user) => {
            console.log("User:", user.nome);
            return getOrders(user.id);
        })
        .then((orders) => {
            console.log("Orders:", orders.length);
            return getOrderDetails(orders[0].id);
        })
        .then((details) => {
            console.log("Details:", details);
            return details;
        })
        .catch((error) => {
            console.error("Errore:", error);
        });
}

// ✅ Con async/await (MODERNO - CONSIGLIATO! 🌟)
async function fetchUserAsync(id) {
    try {
        const user = await getUser(id);
        console.log("User:", user.nome);
        
        const orders = await getOrders(user.id);
        console.log("Orders:", orders.length);
        
        const details = await getOrderDetails(orders[0].id);
        console.log("Details:", details);
        
        return details;
    } catch (error) {
        console.error("Errore:", error);
    }
}

// Molto più leggibile! Sembra codice sincrono! ✅

3
Error Handling con try/catch

try/catch
async function fetchData(url) {
    try {
        const response = await fetch(url);
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const data = await response.json();
        return data;
        
    } catch (error) {
        console.error("Errore nel fetch:", error.message);
        // Gestione errore
        return null;
    } finally {
        console.log("Fetch completato");
    }
}

// Uso
const data = await fetchData("https://api.example.com/users");
if (data) {
    console.log("Dati:", data);
} else {
    console.log("Nessun dato");
}

// Esempio: Validazione e retry
async function fetchWithRetry(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url);
            
            if (response.ok) {
                return await response.json();
            }
            
            console.log(`Tentativo ${i + 1} fallito`);
            
        } catch (error) {
            if (i === retries - 1) throw error;  // Ultimo tentativo
            console.log(`Errore, riprovo... (${i + 1}/${retries})`);
        }
    }
}

// Uso
try {
    const data = await fetchWithRetry("https://api.example.com/data");
    console.log(data);
} catch (error) {
    console.error("Tutti i tentativi falliti:", error);
}

🌐 Fetch API

fetch() è l'API moderna per fare chiamate HTTP. Ritorna una Promise!

1
GET Request

fetch() GET
// Sintassi base
async function getUsers() {
    try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users");
        
        // Verifica status
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        // Parse JSON
        const users = await response.json();
        
        console.log("Utenti:", users);
        return users;
        
    } catch (error) {
        console.error("Errore fetch:", error);
    }
}

// Uso
const users = await getUsers();

// Con headers
async function getUsersWithHeaders() {
    const response = await fetch("https://api.example.com/users", {
        headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer token123"
        }
    });
    
    return await response.json();
}

2
POST/PUT/DELETE Requests

fetch() POST/PUT/DELETE
// POST - Crea risorsa
async function createUser(userData) {
    try {
        const response = await fetch("https://api.example.com/users", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(userData)
        });
        
        const newUser = await response.json();
        return newUser;
        
    } catch (error) {
        console.error("Errore creazione:", error);
    }
}

// Uso
const newUser = await createUser({
    nome: "Mario",
    email: "mario@example.com"
});

// PUT - Aggiorna risorsa
async function updateUser(id, updates) {
    const response = await fetch(`https://api.example.com/users/${id}`, {
        method: "PUT",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(updates)
    });
    
    return await response.json();
}

// Uso
await updateUser(1, { nome: "Mario Updated" });

// DELETE - Elimina risorsa
async function deleteUser(id) {
    const response = await fetch(`https://api.example.com/users/${id}`, {
        method: "DELETE"
    });
    
    if (response.ok) {
        console.log("Utente eliminato");
    }
}

// Uso
await deleteUser(1);

3
Gestione Response Completa

Response Handling
async function fetchWithFullHandling(url) {
    try {
        const response = await fetch(url);
        
        // Status code
        console.log("Status:", response.status);
        console.log("OK:", response.ok);  // true se 200-299
        
        // Headers
        console.log("Content-Type:", response.headers.get("Content-Type"));
        
        // Parse in base a Content-Type
        const contentType = response.headers.get("Content-Type");
        
        let data;
        if (contentType?.includes("application/json")) {
            data = await response.json();
        } else if (contentType?.includes("text/")) {
            data = await response.text();
        } else {
            data = await response.blob();  // File binario
        }
        
        return { status: response.status, data };
        
    } catch (error) {
        // Network error o parsing error
        console.error("Errore:", error);
        throw error;
    }
}

// Esempio completo con loading e error states
async function fetchUserProfile(userId) {
    const state = {
        loading: true,
        data: null,
        error: null
    };
    
    try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error(`Error ${response.status}: ${response.statusText}`);
        }
        
        state.data = await response.json();
        
    } catch (error) {
        state.error = error.message;
    } finally {
        state.loading = false;
    }
    
    return state;
}

// Uso
const profile = await fetchUserProfile(1);

if (profile.loading) {
    console.log("Caricamento...");
} else if (profile.error) {
    console.error("Errore:", profile.error);
} else {
    console.log("Dati:", profile.data);
}

🔀 Promise Multiple

Gestisci più Promise contemporaneamente con metodi statici.

1
Promise.all() - Tutte le Promise

Aspetta che TUTTE le Promise si risolvano (o una fallisca).

Promise.all()
// Esegue in parallelo, aspetta tutte
const promise1 = fetch("/api/users");
const promise2 = fetch("/api/posts");
const promise3 = fetch("/api/comments");

try {
    const [users, posts, comments] = await Promise.all([
        promise1.then(r => r.json()),
        promise2.then(r => r.json()),
        promise3.then(r => r.json())
    ]);
    
    console.log("Users:", users);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
    
} catch (error) {
    // Se UNA sola fallisce, tutto fallisce
    console.error("Errore:", error);
}

// Esempio pratico: Dashboard data
async function loadDashboard() {
    const startTime = Date.now();
    
    try {
        const [userData, salesData, analyticsData] = await Promise.all([
            fetchUserData(),
            fetchSalesData(),
            fetchAnalytics()
        ]);
        
        const loadTime = Date.now() - startTime;
        console.log(`Dati caricati in ${loadTime}ms`);
        
        return { userData, salesData, analyticsData };
        
    } catch (error) {
        console.error("Errore caricamento dashboard:", error);
    }
}

// Molto più veloce che in sequenza! ✅

2
Promise.race() - Prima che Finisce

Ritorna la prima Promise che si risolve (o fallisce).

Promise.race()
// Prima che finisce vince
const slow = new Promise(resolve =>
    setTimeout(() => resolve("Lento"), 3000)
);

const fast = new Promise(resolve =>
    setTimeout(() => resolve("Veloce"), 1000)
);

const result = await Promise.race([slow, fast]);
console.log(result);  // "Veloce" (dopo 1 secondo)

// Caso d'uso: Timeout
async function fetchWithTimeout(url, timeout = 5000) {
    const fetchPromise = fetch(url);
    
    const timeoutPromise = new Promise((_, reject) =>
        setTimeout(() => reject(new Error("Timeout!")), timeout)
    );
    
    try {
        const response = await Promise.race([fetchPromise, timeoutPromise]);
        return await response.json();
    } catch (error) {
        if (error.message === "Timeout!") {
            console.error("Request troppo lenta!");
        }
        throw error;
    }
}

// Uso
try {
    const data = await fetchWithTimeout("/api/slow-endpoint", 3000);
    console.log(data);
} catch (error) {
    console.error("Errore:", error.message);
}

3
Promise.allSettled() - Tutte Completate

Aspetta tutte, anche se alcune falliscono.

Promise.allSettled()
// Non fallisce mai - aspetta tutte
const promises = [
    fetch("/api/users").then(r => r.json()),
    fetch("/api/posts").then(r => r.json()),
    fetch("/api/invalid").then(r => r.json())  // Fallisce
];

const results = await Promise.allSettled(promises);

results.forEach((result, index) => {
    if (result.status === "fulfilled") {
        console.log(`Promise ${index}: Success`, result.value);
    } else {
        console.log(`Promise ${index}: Failed`, result.reason);
    }
});

// Output:
// Promise 0: Success [users data]
// Promise 1: Success [posts data]
// Promise 2: Failed TypeError: Failed to fetch

// Caso d'uso: Batch operations
async function deleteMultipleUsers(userIds) {
    const deletePromises = userIds.map(id =>
        fetch(`/api/users/${id}`, { method: "DELETE" })
    );
    
    const results = await Promise.allSettled(deletePromises);
    
    const succeeded = results.filter(r => r.status === "fulfilled").length;
    const failed = results.filter(r => r.status === "rejected").length;
    
    console.log(`Eliminati: ${succeeded}, Falliti: ${failed}`);
    
    return { succeeded, failed, results };
}

4
Promise.any() - Prima che Riesce

Ritorna la prima Promise che si risolve con successo.

Promise.any()
// Prima che RISOLVE vince (ignora errori)
const promises = [
    fetch("/api/primary").then(r => r.json()),
    fetch("/api/fallback1").then(r => r.json()),
    fetch("/api/fallback2").then(r => r.json())
];

try {
    const data = await Promise.any(promises);
    console.log("Prima disponibile:", data);
} catch (error) {
    // Fallisce solo se TUTTE falliscono
    console.error("Tutte le API sono down!");
}

// Caso d'uso: API redundancy
async function fetchFromMultipleSources(endpoint) {
    const sources = [
        `https://api1.example.com${endpoint}`,
        `https://api2.example.com${endpoint}`,
        `https://api3.example.com${endpoint}`
    ];
    
    const promises = sources.map(url =>
        fetch(url).then(r => r.json())
    );
    
    // Usa la prima API che risponde
    return await Promise.any(promises);
}

🚀 Esempio Completo: API Client

API Client Professionale
// === API CLIENT CLASS ===
class APIClient {
    constructor(baseURL, options = {}) {
        this.baseURL = baseURL;
        this.timeout = options.timeout || 10000;
        this.retries = options.retries || 3;
        this.headers = options.headers || {};
    }
    
    // Request con timeout
    async fetchWithTimeout(url, options, timeout) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);
        
        try {
            const response = await fetch(url, {
                ...options,
                signal: controller.signal
            });
            clearTimeout(timeoutId);
            return response;
        } catch (error) {
            clearTimeout(timeoutId);
            if (error.name === 'AbortError') {
                throw new Error('Request timeout');
            }
            throw error;
        }
    }
    
    // Request con retry
    async request(endpoint, options = {}) {
        const url = `${this.baseURL}${endpoint}`;
        
        const config = {
            headers: {
                'Content-Type': 'application/json',
                ...this.headers,
                ...options.headers
            },
            ...options
        };
        
        let lastError;
        
        for (let attempt = 0; attempt < this.retries; attempt++) {
            try {
                console.log(`Tentativo ${attempt + 1}/${this.retries}`);
                
                const response = await this.fetchWithTimeout(
                    url,
                    config,
                    this.timeout
                );
                
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                
                const data = await response.json();
                return { success: true, data };
                
            } catch (error) {
                lastError = error;
                console.error(`Tentativo ${attempt + 1} fallito:`, error.message);
                
                if (attempt < this.retries - 1) {
                    // Exponential backoff
                    const delay = Math.pow(2, attempt) * 1000;
                    console.log(`Riprovo tra ${delay}ms...`);
                    await new Promise(resolve => setTimeout(resolve, delay));
                }
            }
        }
        
        return {
            success: false,
            error: lastError.message
        };
    }
    
    // GET
    async get(endpoint) {
        return await this.request(endpoint, { method: 'GET' });
    }
    
    // POST
    async post(endpoint, data) {
        return await this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }
    
    // PUT
    async put(endpoint, data) {
        return await this.request(endpoint, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }
    
    // DELETE
    async delete(endpoint) {
        return await this.request(endpoint, { method: 'DELETE' });
    }
    
    // Batch requests
    async batchGet(endpoints) {
        const promises = endpoints.map(endpoint => this.get(endpoint));
        return await Promise.allSettled(promises);
    }
}

// === USO ===

// Inizializza client
const api = new APIClient('https://jsonplaceholder.typicode.com', {
    timeout: 5000,
    retries: 3,
    headers: {
        'Authorization': 'Bearer token123'
    }
});

// === ESEMPIO 1: GET singolo ===
async function getUser(id) {
    console.log("=== GET USER ===");
    const result = await api.get(`/users/${id}`);
    
    if (result.success) {
        console.log("Utente:", result.data);
    } else {
        console.error("Errore:", result.error);
    }
}

await getUser(1);

// === ESEMPIO 2: POST ===
async function createPost() {
    console.log("\n=== CREATE POST ===");
    const newPost = {
        title: "Nuovo Post",
        body: "Contenuto del post",
        userId: 1
    };
    
    const result = await api.post('/posts', newPost);
    
    if (result.success) {
        console.log("Post creato:", result.data);
    } else {
        console.error("Errore:", result.error);
    }
}

await createPost();

// === ESEMPIO 3: Batch requests ===
async function loadDashboard() {
    console.log("\n=== LOAD DASHBOARD ===");
    const startTime = Date.now();
    
    const endpoints = ['/users/1', '/posts', '/comments'];
    const results = await api.batchGet(endpoints);
    
    const loadTime = Date.now() - startTime;
    console.log(`Caricamento completato in ${loadTime}ms`);
    
    results.forEach((result, index) => {
        if (result.status === 'fulfilled' && result.value.success) {
            console.log(`✅ Endpoint ${index + 1}: Success`);
        } else {
            console.log(`❌ Endpoint ${index + 1}: Failed`);
        }
    });
    
    return results;
}

await loadDashboard();

// === ESEMPIO 4: Error handling completo ===
async function complexOperation() {
    console.log("\n=== COMPLEX OPERATION ===");
    
    try {
        // Step 1: Get user
        console.log("Step 1: Carico utente...");
        const userResult = await api.get('/users/1');
        
        if (!userResult.success) {
            throw new Error("Impossibile caricare utente");
        }
        
        const user = userResult.data;
        console.log("Utente:", user.name);
        
        // Step 2: Get user's posts
        console.log("Step 2: Carico post...");
        const postsResult = await api.get(`/users/${user.id}/posts`);
        
        if (!postsResult.success) {
            throw new Error("Impossibile caricare post");
        }
        
        const posts = postsResult.data;
        console.log(`Post trovati: ${posts.length}`);
        
        // Step 3: Get comments for first post
        if (posts.length > 0) {
            console.log("Step 3: Carico commenti...");
            const commentsResult = await api.get(`/posts/${posts[0].id}/comments`);
            
            if (commentsResult.success) {
                const comments = commentsResult.data;
                console.log(`Commenti trovati: ${comments.length}`);
            }
        }
        
        console.log("✅ Operazione completata con successo!");
        
    } catch (error) {
        console.error("❌ Operazione fallita:", error.message);
    } finally {
        console.log("Pulizia risorse...");
    }
}

await complexOperation();

// Output:
// === GET USER ===
// Tentativo 1/3
// Utente: {id: 1, name: "Leanne Graham", ...}
// 
// === CREATE POST ===
// Tentativo 1/3
// Post creato: {id: 101, title: "Nuovo Post", ...}
// 
// === LOAD DASHBOARD ===
// Tentativo 1/3
// Tentativo 1/3
// Tentativo 1/3
// Caricamento completato in 245ms
// ✅ Endpoint 1: Success
// ✅ Endpoint 2: Success
// ✅ Endpoint 3: Success
// 
// === COMPLEX OPERATION ===
// Step 1: Carico utente...
// Tentativo 1/3
// Utente: Leanne Graham
// Step 2: Carico post...
// Tentativo 1/3
// Post trovati: 10
// Step 3: Carico commenti...
// Tentativo 1/3
// Commenti trovati: 5
// ✅ Operazione completata con successo!
// Pulizia risorse...

💡 Features Implementate
  • Timeout - AbortController per limitare tempo
  • Retry logic - Exponential backoff
  • Error handling - try/catch completo
  • Batch requests - Promise.allSettled
  • REST methods - GET, POST, PUT, DELETE
  • Headers config - Authorization, Content-Type
  • Response parsing - JSON automatico
  • Status checking - response.ok

✅ Best Practices

🌟 Regole d'Oro Async/Await

1
Usa async/await invece di then

// ✅ MODERNO - async/await
async function getData() {
    try {
        const response = await fetch(url);
        const data = await response.json();
        return data;
    } catch (error) {
        console.error(error);
    }
}

// ❌ EVITA - then (meno leggibile)
function getData() {
    return fetch(url)
        .then(r => r.json())
        .then(data => data)
        .catch(error => console.error(error));
}

2
Sempre try/catch

// ✅ Con error handling
async function safeFetch() {
    try {
        const data = await fetch(url);
        return data;
    } catch (error) {
        console.error("Errore:", error);
        return null;
    }
}

// ❌ Senza error handling (pericoloso!)
async function unsafeFetch() {
    const data = await fetch(url);  // Può crashare!
    return data;
}

3
Promise.all per Parallelismo

// ✅ PARALLELO (veloce)
const [users, posts, comments] = await Promise.all([
    fetchUsers(),
    fetchPosts(),
    fetchComments()
]);
// Tempo: ~1 secondo (simultaneo)

// ❌ SEQUENZIALE (lento)
const users = await fetchUsers();     // 1 secondo
const posts = await fetchPosts();     // 1 secondo
const comments = await fetchComments(); // 1 secondo
// Tempo totale: 3 secondi!

4
Verifica response.ok

// ✅ Controlla status
const response = await fetch(url);

if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
}

const data = await response.json();

// ❌ Senza controllo
const response = await fetch(url);
const data = await response.json();  // Fallisce su 404!

🎯 Riepilogo Lezione

Concetto Quando Usare Sintassi
async/await ⭐ SEMPRE (moderno) await promise
try/catch Gestire errori try { await } catch(e) {}
fetch() Chiamate HTTP await fetch(url)
Promise.all() Più Promise parallele await Promise.all([...])
Promise.race() Prima che finisce await Promise.race([...])
Promise.allSettled() Tutte (anche errori) await Promise.allSettled([...])

🎉 CONGRATULAZIONI! 🎉

Hai completato tutte le 16 lezioni del corso JavaScript!

Ora conosci:

✅ Variabili e Tipi Dati
✅ Operatori ed Espressioni
✅ Stringhe e Array
✅ Oggetti e Metodi
✅ Condizionali e Cicli
✅ Funzioni Base e Avanzate
✅ Array Methods (map/filter/reduce)
✅ DOM Manipulation
✅ Eventi
✅ Destructuring & Spread
✅ Promise & Async/Await
✅ Fetch API

🚀 Sei pronto per costruire applicazioni JavaScript moderne!

🏆 Corso JavaScript Completato!

Lezione 16 di 16 - 100% Completato

Continua a praticare e costruisci progetti reali per consolidare le tue competenze! 💪

Torna in alto