JavaScript Programming Labs

Master JavaScript through hands-on coding challenges. Write real code, get instant feedback, and build practical web development skills.

Asynchronous JavaScript - Module 5

Master Promises, async/await, and the Fetch API for handling asynchronous operations.

Lab 13: Promises
Advanced
Coding Challenge
Your Task: Learn to work with Promises - objects representing the eventual completion (or failure) of an asynchronous operation. Promises are fundamental to modern JavaScript!

Detailed Requirements:

1. Create a new Promise - A Promise takes an executor function with resolve and reject callbacks.
const myPromise = new Promise((resolve, reject) => { // Simulate async operation setTimeout(() => { const success = true; if (success) { resolve("Operation completed!"); // Fulfills the promise } else { reject("Operation failed!"); // Rejects the promise } }, 1000); });
2. Handle Promise resolution with .then() - Chain .then() to handle successful results.
myPromise .then(result => { console.log("Success:", result); return result.toUpperCase(); // Return value for next .then() }) .then(upperResult => { console.log("Transformed:", upperResult); });
3. Handle Promise rejection with .catch() - Catch errors at any point in the chain.
myPromise .then(result => { console.log(result); throw new Error("Something went wrong!"); }) .catch(error => { console.error("Error caught:", error.message); });
4. Use .finally() for cleanup - Runs regardless of success or failure.
myPromise .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => { console.log("Cleanup complete!"); // Always runs });
5. Use Promise.all() for parallel execution - Wait for ALL promises to resolve.
const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = new Promise(resolve => setTimeout(() => resolve(3), 100)); Promise.all([promise1, promise2, promise3]) .then(results => { console.log(results); // [1, 2, 3] }) .catch(error => { // If ANY promise rejects, catch fires });
6. Use Promise.race() for first-to-complete - Resolves/rejects with first settled promise.
const fast = new Promise(resolve => setTimeout(() => resolve("Fast!"), 100)); const slow = new Promise(resolve => setTimeout(() => resolve("Slow!"), 500)); Promise.race([fast, slow]) .then(winner => console.log(winner)); // "Fast!"
💡 Pro Tips:
Promise.resolve(value) creates an already-resolved promise
Promise.reject(error) creates an already-rejected promise
• Always have a .catch() at the end of your chain
Promise.allSettled() waits for all, doesn't short-circuit on rejection
Promise.any() resolves with first fulfilled (ignores rejections)

⚠️ Common Mistakes to Avoid:
• Forgetting to return values in .then() chains
• Not handling rejections (unhandled promise rejection)
• Creating promise inside promise (promise anti-pattern)

Expected Output:
Promise created! Resolved: Data loaded successfully Chained: DATA LOADED SUCCESSFULLY All promises resolved: [1, 2, 3] Race winner: Fast!

Requirements Checklist

Create a new Promise with resolve and reject
Handle resolution with .then()
Handle rejection with .catch()
Use .finally() for cleanup
Use Promise.all() for parallel execution
Use Promise.race() or Promise.any()
Console Output
// Click "Run Code" to execute your JavaScript
Hints & Tips
• Create: new Promise((resolve, reject) => { })
• Handle success: .then(result => { })
• Handle error: .catch(error => { })
• Cleanup: .finally(() => { })
• Parallel: Promise.all([p1, p2, p3])
• Race: Promise.race([p1, p2])
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 14: Async/Await
Advanced
Coding Challenge
Your Task: Master async/await - syntactic sugar over Promises that makes asynchronous code look and behave more like synchronous code.

Detailed Requirements:

1. Create an async function - The async keyword makes a function return a Promise automatically.
async function fetchData() { return "Data fetched!"; // Automatically wrapped in Promise.resolve() } // Arrow function version: const fetchData = async () => { return "Data fetched!"; }; fetchData().then(result => console.log(result));
2. Use await to wait for Promises - Pauses execution until Promise resolves.
async function getData() { console.log("Starting..."); const result = await somePromise; // Waits here console.log("Got result:", result); const result2 = await anotherPromise; // Then waits here console.log("Got result2:", result2); return result + result2; }
3. Handle errors with try/catch - Much cleaner than .catch() chains!
async function fetchWithErrorHandling() { try { const data = await fetchData(); const processed = await processData(data); return processed; } catch (error) { console.error("Error occurred:", error.message); return null; // Or rethrow: throw error; } finally { console.log("Cleanup!"); } }
4. Sequential vs Parallel execution - Await in sequence or use Promise.all for parallel.
// SEQUENTIAL (slower - one after another) async function sequential() { const a = await fetchA(); // Wait for A const b = await fetchB(); // Then wait for B return [a, b]; } // PARALLEL (faster - run simultaneously) async function parallel() { const [a, b] = await Promise.all([ fetchA(), // Start both at once fetchB() ]); return [a, b]; }
5. Async in loops - Be careful with array methods!
// Sequential processing (use for...of) async function processSequentially(items) { for (const item of items) { await processItem(item); // One at a time } } // Parallel processing (use Promise.all + map) async function processParallel(items) { await Promise.all(items.map(item => processItem(item))); }
6. Return values from async functions - Always returns a Promise.
async function calculate() { const a = await getA(); const b = await getB(); return a + b; // Promise that resolves to sum } // Using the result: calculate().then(sum => console.log(sum)); // Or in another async function: const sum = await calculate();
💡 Pro Tips:
await can only be used inside async functions
• Top-level await is available in ES modules
• An async function always returns a Promise
• Use Promise.all() for parallel execution when possible
forEach doesn't work well with async/await - use for...of

⚠️ Common Mistakes to Avoid:
• Using await outside an async function
• Sequential awaits when parallel is possible (performance)
• Not handling errors (unhandled rejection)
• Using forEach with async (use for...of or Promise.all)

Expected Output:
Starting async function... Data fetched: User data Processed: USER DATA All done in parallel: [1, 2, 3] Loop complete!

Requirements Checklist

Create an async function
Use await to wait for a Promise
Handle errors with try/catch
Use Promise.all with await for parallel execution
Use async with arrow function syntax
Return a value from an async function
Console Output
// Click "Run Code" to execute your JavaScript
Hints & Tips
• Async function: async function name() { }
• Async arrow: const fn = async () => { }
• Wait for promise: const result = await promise
• Error handling: try { await... } catch (e) { }
• Parallel: await Promise.all([p1, p2])
• Return: return value (wrapped in Promise)
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 15: Fetch API
Advanced
Coding Challenge
Your Task: Master the Fetch API - the modern way to make HTTP requests in JavaScript. Fetch returns Promises and works great with async/await!

Detailed Requirements:

1. Make a basic GET request - Fetch returns a Promise that resolves to a Response object.
// Basic fetch (GET by default) fetch("https://api.example.com/data") .then(response => response.json()) // Parse JSON body .then(data => console.log(data)) .catch(error => console.error("Error:", error)); // With async/await (preferred): async function getData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; }
2. Check response status - Fetch doesn't reject on HTTP errors! Check response.ok.
async function fetchWithStatusCheck(url) { const response = await fetch(url); if (!response.ok) { throw new Error(\`HTTP error! Status: \${response.status}\`); } return await response.json(); } // Response properties: // response.ok - true if status 200-299 // response.status - HTTP status code (200, 404, etc.) // response.headers - Response headers
3. Make a POST request with JSON body - Send data to an API.
async function createUser(userData) { const response = await fetch("https://api.example.com/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(userData) }); if (!response.ok) { throw new Error("Failed to create user"); } return await response.json(); } // Usage: createUser({ name: "John", email: "john@example.com" });
4. Handle different response types - JSON, text, blob, etc.
// JSON response const jsonData = await response.json(); // Plain text const textData = await response.text(); // Binary data (images, files) const blobData = await response.blob(); // Form data const formData = await response.formData(); // Array buffer const buffer = await response.arrayBuffer();
5. Add headers and options - Authentication, custom headers, etc.
const response = await fetch(url, { method: "GET", // GET, POST, PUT, DELETE, PATCH headers: { "Authorization": "Bearer token123", "Content-Type": "application/json", "X-Custom-Header": "value" }, mode: "cors", // cors, no-cors, same-origin credentials: "include", // include, same-origin, omit cache: "no-cache" // default, no-cache, reload, etc. });
6. Handle errors properly - Network errors and HTTP errors.
async function robustFetch(url) { try { const response = await fetch(url); if (!response.ok) { // HTTP error (4xx, 5xx) const errorBody = await response.text(); throw new Error(\`HTTP \${response.status}: \${errorBody}\`); } return await response.json(); } catch (error) { if (error.name === "TypeError") { // Network error (no internet, CORS, etc.) console.error("Network error:", error.message); } throw error; } }
💡 Pro Tips:
fetch() only rejects on network failure, NOT on HTTP errors
• Always check response.ok or response.status
response.json() also returns a Promise!
• Use AbortController to cancel requests
• For file uploads, use FormData instead of JSON

⚠️ Common Mistakes to Avoid:
• Forgetting to await response.json()
• Not checking response.ok before parsing
• Missing Content-Type header for POST/PUT
• Forgetting to JSON.stringify() the body

Expected Output:
Fetching data from API... Status: 200 OK Data received: { id: 1, name: "John" } POST successful: { id: 2, created: true } Error handled gracefully!

Requirements Checklist

Make a basic GET request with fetch()
Parse JSON response with response.json()
Check response.ok or response.status
Make a POST request with JSON body
Set headers (Content-Type, Authorization, etc.)
Handle errors with try/catch
Console Output
// Click "Run Code" to execute your JavaScript
Hints & Tips
• GET: fetch(url)
• Parse JSON: await response.json()
• Check status: if (!response.ok) throw Error
• POST: fetch(url, { method: "POST", body: JSON.stringify(data) })
• Headers: headers: { "Content-Type": "application/json" }
• Error handling: try { await fetch() } catch (e) { }
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below