javascript

examples

examples.js
/**
 * ========================================
 * 9.3 ASYNC/AWAIT - CODE EXAMPLES
 * ========================================
 */

// Helper function for delays
function delay(ms, value) {
  return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}

/**
 * EXAMPLE 1: Basic Async Function
 */
console.log('--- Example 1: Basic Async Function ---');

async function greet(name) {
  return `Hello, ${name}!`;
}

// Async functions always return a promise
greet('Alice').then((message) => console.log(message));

// Equivalent to:
function greetPromise(name) {
  return Promise.resolve(`Hello, ${name}!`);
}

/**
 * EXAMPLE 2: Using await
 */
console.log('\n--- Example 2: Using await ---');

async function fetchUser(id) {
  console.log('Fetching user...');
  const user = await delay(100, { id, name: `User ${id}` });
  console.log('User fetched:', user);
  return user;
}

fetchUser(1);

/**
 * EXAMPLE 3: Sequential vs Parallel
 */
console.log('\n--- Example 3: Sequential vs Parallel ---');

async function sequential() {
  const start = Date.now();
  const a = await delay(50, 'A');
  const b = await delay(50, 'B');
  const c = await delay(50, 'C');
  console.log(`Sequential: ${Date.now() - start}ms`, [a, b, c]);
}

async function parallel() {
  const start = Date.now();
  const [a, b, c] = await Promise.all([
    delay(50, 'A'),
    delay(50, 'B'),
    delay(50, 'C'),
  ]);
  console.log(`Parallel: ${Date.now() - start}ms`, [a, b, c]);
}

sequential();
parallel();

/**
 * EXAMPLE 4: Error Handling with try/catch
 */
console.log('\n--- Example 4: Error Handling ---');

async function riskyOperation(shouldFail) {
  await delay(50);
  if (shouldFail) {
    throw new Error('Operation failed!');
  }
  return 'Success!';
}

async function handleErrors() {
  // Success case
  try {
    const result = await riskyOperation(false);
    console.log('Result:', result);
  } catch (error) {
    console.error('Error:', error.message);
  }

  // Failure case
  try {
    const result = await riskyOperation(true);
    console.log('Result:', result);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

handleErrors();

/**
 * EXAMPLE 5: Multiple try/catch Blocks
 */
console.log('\n--- Example 5: Multiple try/catch ---');

async function processData() {
  let data;

  // Step 1: Fetch data
  try {
    data = await delay(50, { value: 42 });
    console.log('Data fetched:', data);
  } catch (error) {
    console.error('Fetch failed, using default');
    data = { value: 0 };
  }

  // Step 2: Process data
  try {
    const processed = { ...data, processed: true };
    console.log('Processed:', processed);
    return processed;
  } catch (error) {
    console.error('Processing failed:', error);
    return null;
  }
}

processData();

/**
 * EXAMPLE 6: Catching Errors with .catch()
 */
console.log('\n--- Example 6: Inline Error Handling ---');

async function withFallback() {
  // Inline error handling with .catch()
  const data = await delay(50, 'primary').catch(() => 'fallback');
  console.log('Data:', data);

  // For conditional error handling
  const result = await Promise.reject(new Error('Failed')).catch((e) => {
    console.log('Handled:', e.message);
    return 'default value';
  });
  console.log('Result:', result);
}

withFallback();

/**
 * EXAMPLE 7: Error Tuple Pattern
 */
console.log('\n--- Example 7: Error Tuple Pattern ---');

async function safeAwait(promise) {
  try {
    const result = await promise;
    return [null, result];
  } catch (error) {
    return [error, null];
  }
}

async function useTuplePattern() {
  const [error1, result1] = await safeAwait(delay(50, 'Success!'));
  if (error1) {
    console.log('Error:', error1.message);
  } else {
    console.log('Result:', result1);
  }

  const [error2, result2] = await safeAwait(
    Promise.reject(new Error('Failed!'))
  );
  if (error2) {
    console.log('Error:', error2.message);
  } else {
    console.log('Result:', result2);
  }
}

useTuplePattern();

/**
 * EXAMPLE 8: Async Arrow Functions
 */
console.log('\n--- Example 8: Arrow Functions ---');

// Arrow function syntax
const fetchData = async (url) => {
  const data = await delay(50, { url, content: 'data' });
  return data;
};

// In callbacks
const items = [1, 2, 3];
const processItems = async () => {
  const results = await Promise.all(
    items.map(async (item) => {
      return await delay(50, item * 2);
    })
  );
  console.log('Processed items:', results);
};

fetchData('/api').then((data) => console.log('Arrow result:', data));
processItems();

/**
 * EXAMPLE 9: Async Methods in Classes
 */
console.log('\n--- Example 9: Class Methods ---');

class DataService {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }

  async fetch(endpoint) {
    console.log(`Fetching ${this.baseUrl}${endpoint}`);
    return await delay(50, { endpoint, data: 'result' });
  }

  async fetchMultiple(endpoints) {
    return Promise.all(endpoints.map((e) => this.fetch(e)));
  }
}

const service = new DataService('https://api.example.com');
service.fetch('/users').then((data) => console.log('Service result:', data));

/**
 * EXAMPLE 10: Loops with async/await
 */
console.log('\n--- Example 10: Loops ---');

// Sequential with for...of
async function processSequentially(items) {
  console.log('Processing sequentially:');
  for (const item of items) {
    const result = await delay(30, `Processed ${item}`);
    console.log(result);
  }
}

// Parallel with map + Promise.all
async function processInParallel(items) {
  console.log('Processing in parallel:');
  const results = await Promise.all(
    items.map((item) => delay(30, `Processed ${item}`))
  );
  console.log(results);
}

processSequentially([1, 2, 3]);
processInParallel(['A', 'B', 'C']);

/**
 * EXAMPLE 11: forEach Does NOT Work
 */
console.log('\n--- Example 11: forEach Problem ---');

async function forEachProblem() {
  const items = [1, 2, 3];

  // ❌ This doesn't wait properly
  console.log('forEach (broken):');
  let completed = 0;
  items.forEach(async (item) => {
    await delay(50, null);
    completed++;
    console.log(`  Item ${item} done`);
  });
  console.log(`  Completed: ${completed}`); // 0! forEach doesn't wait

  // ✅ Use for...of instead
  await delay(200); // Wait for forEach to finish for demo
  console.log('for...of (correct):');
  for (const item of items) {
    await delay(50, null);
    console.log(`  Item ${item} done`);
  }
}

forEachProblem();

/**
 * EXAMPLE 12: Retry Pattern
 */
console.log('\n--- Example 12: Retry Pattern ---');

async function retry(fn, attempts, delayMs) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === attempts - 1) throw error;
      console.log(`Attempt ${i + 1} failed, retrying...`);
      await delay(delayMs);
    }
  }
}

let attemptCount = 0;
async function flakyFunction() {
  attemptCount++;
  if (attemptCount < 3) {
    throw new Error('Temporary failure');
  }
  return 'Success!';
}

retry(flakyFunction, 5, 50).then((result) =>
  console.log('Retry result:', result)
);

/**
 * EXAMPLE 13: Timeout Pattern
 */
console.log('\n--- Example 13: Timeout Pattern ---');

async function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) => {
    setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
  });
  return Promise.race([promise, timeout]);
}

async function demonstrateTimeout() {
  // Fast enough
  try {
    const result = await withTimeout(delay(50, 'Quick!'), 200);
    console.log('Fast result:', result);
  } catch (error) {
    console.log('Fast error:', error.message);
  }

  // Too slow
  try {
    const result = await withTimeout(delay(300, 'Slow...'), 100);
    console.log('Slow result:', result);
  } catch (error) {
    console.log('Slow error:', error.message);
  }
}

demonstrateTimeout();

/**
 * EXAMPLE 14: Polling Pattern
 */
console.log('\n--- Example 14: Polling Pattern ---');

let pollCount = 0;
async function checkStatus() {
  pollCount++;
  return {
    ready: pollCount >= 3,
    progress: pollCount * 33,
  };
}

async function poll(fn, interval, maxAttempts) {
  for (let i = 0; i < maxAttempts; i++) {
    const result = await fn();
    console.log(`Poll ${i + 1}: ${result.progress}%`);
    if (result.ready) {
      return result;
    }
    await delay(interval);
  }
  throw new Error('Polling timeout');
}

poll(checkStatus, 50, 5).then((result) =>
  console.log('Poll complete:', result)
);

/**
 * EXAMPLE 15: Batched Processing
 */
console.log('\n--- Example 15: Batched Processing ---');

async function processBatch(items, batchSize, processor) {
  const results = [];

  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    console.log(`Processing batch: ${batch}`);

    const batchResults = await Promise.all(batch.map(processor));
    results.push(...batchResults);
  }

  return results;
}

processBatch([1, 2, 3, 4, 5, 6, 7, 8], 3, (n) => delay(50, n * 2)).then(
  (results) => console.log('Batch results:', results)
);

/**
 * EXAMPLE 16: Async Generator
 */
console.log('\n--- Example 16: Async Generator ---');

async function* asyncGenerator(count) {
  for (let i = 1; i <= count; i++) {
    const value = await delay(50, i);
    yield value;
  }
}

async function consumeGenerator() {
  console.log('Consuming async generator:');
  for await (const value of asyncGenerator(3)) {
    console.log('  Generated:', value);
  }
}

consumeGenerator();

/**
 * EXAMPLE 17: Concurrent Task Limiter
 */
console.log('\n--- Example 17: Concurrency Limiter ---');

class ConcurrencyLimiter {
  constructor(limit) {
    this.limit = limit;
    this.running = 0;
    this.queue = [];
  }

  async run(fn) {
    if (this.running >= this.limit) {
      await new Promise((resolve) => this.queue.push(resolve));
    }

    this.running++;
    try {
      return await fn();
    } finally {
      this.running--;
      if (this.queue.length > 0) {
        const next = this.queue.shift();
        next();
      }
    }
  }
}

async function demonstrateLimiter() {
  const limiter = new ConcurrencyLimiter(2);
  const tasks = [1, 2, 3, 4, 5].map((i) => {
    return limiter.run(async () => {
      console.log(`Task ${i} started`);
      await delay(100);
      console.log(`Task ${i} done`);
      return i;
    });
  });

  const results = await Promise.all(tasks);
  console.log('Limiter results:', results);
}

demonstrateLimiter();

/**
 * EXAMPLE 18: Async Initialization
 */
console.log('\n--- Example 18: Async Initialization ---');

class AsyncDatabase {
  #connection = null;
  #ready = null;

  constructor(config) {
    this.config = config;
    this.#ready = this.#initialize();
  }

  async #initialize() {
    console.log('Connecting to database...');
    await delay(100);
    this.#connection = { connected: true };
    console.log('Database connected!');
  }

  async query(sql) {
    await this.#ready; // Wait for initialization
    console.log(`Query: ${sql}`);
    return [{ id: 1, name: 'Result' }];
  }
}

const db = new AsyncDatabase({ host: 'localhost' });
db.query('SELECT * FROM users').then((results) =>
  console.log('Query results:', results)
);

/**
 * EXAMPLE 19: Async Event Handler
 */
console.log('\n--- Example 19: Async Event Handler ---');

class AsyncEventEmitter {
  #handlers = new Map();

  on(event, handler) {
    if (!this.#handlers.has(event)) {
      this.#handlers.set(event, []);
    }
    this.#handlers.get(event).push(handler);
  }

  async emit(event, ...args) {
    const handlers = this.#handlers.get(event) || [];
    const results = [];

    for (const handler of handlers) {
      const result = await handler(...args);
      results.push(result);
    }

    return results;
  }
}

async function demonstrateAsyncEvents() {
  const emitter = new AsyncEventEmitter();

  emitter.on('data', async (data) => {
    await delay(50);
    return `Handler 1: ${data}`;
  });

  emitter.on('data', async (data) => {
    await delay(30);
    return `Handler 2: ${data}`;
  });

  const results = await emitter.emit('data', 'test');
  console.log('Event results:', results);
}

demonstrateAsyncEvents();

/**
 * EXAMPLE 20: Real-World Example - API Client
 */
console.log('\n--- Example 20: Complete API Client ---');

class APIClient {
  constructor(baseUrl, options = {}) {
    this.baseUrl = baseUrl;
    this.timeout = options.timeout || 5000;
    this.retries = options.retries || 3;
  }

  async #request(endpoint, options = {}) {
    const { method = 'GET', body, headers = {} } = options;

    // Simulate network request
    await delay(50);

    if (endpoint.includes('error')) {
      throw new Error(`Request failed: ${endpoint}`);
    }

    return {
      ok: true,
      status: 200,
      data: { endpoint, method, message: 'Success' },
    };
  }

  async #requestWithRetry(endpoint, options, attempts = this.retries) {
    try {
      return await this.#request(endpoint, options);
    } catch (error) {
      if (attempts <= 1) throw error;
      console.log(`Retrying ${endpoint}...`);
      await delay(100);
      return this.#requestWithRetry(endpoint, options, attempts - 1);
    }
  }

  async get(endpoint) {
    return this.#requestWithRetry(endpoint, { method: 'GET' });
  }

  async post(endpoint, data) {
    return this.#requestWithRetry(endpoint, {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }

  async getMultiple(endpoints) {
    const results = await Promise.allSettled(endpoints.map((e) => this.get(e)));

    return results.map((result, i) => ({
      endpoint: endpoints[i],
      success: result.status === 'fulfilled',
      data: result.status === 'fulfilled' ? result.value.data : null,
      error: result.status === 'rejected' ? result.reason.message : null,
    }));
  }
}

async function demonstrateAPIClient() {
  const api = new APIClient('https://api.example.com');

  // Single request
  const user = await api.get('/users/1');
  console.log('Single request:', user.data);

  // Multiple requests
  const results = await api.getMultiple(['/users', '/posts', '/error']);
  console.log('Multiple requests:', results);
}

demonstrateAPIClient();

console.log('\n========================================');
console.log('End of Async/Await Examples');
console.log('========================================');
Examples - JavaScript Tutorial | DeepML