javascript

examples

examples.js
/**
 * ========================================
 * 9.2 PROMISES - CODE EXAMPLES
 * ========================================
 */

/**
 * EXAMPLE 1: Creating a Promise
 * Basic promise construction
 */
console.log('--- Example 1: Creating a Promise ---');

const simplePromise = new Promise((resolve, reject) => {
  const success = true;

  setTimeout(() => {
    if (success) {
      resolve('Operation successful!');
    } else {
      reject(new Error('Operation failed!'));
    }
  }, 100);
});

simplePromise
  .then((result) => console.log('Result:', result))
  .catch((error) => console.error('Error:', error.message));

/**
 * EXAMPLE 2: Promise States
 * Demonstrating pending, fulfilled, rejected
 */
console.log('\n--- Example 2: Promise States ---');

// Pending promise
const pendingPromise = new Promise(() => {
  // Never resolves or rejects
});
console.log('Pending promise:', pendingPromise);

// Immediately fulfilled
const fulfilledPromise = Promise.resolve('Immediate value');
console.log('Fulfilled promise:', fulfilledPromise);

// Immediately rejected
const rejectedPromise = Promise.reject(new Error('Immediate error'));
rejectedPromise.catch(() => {}); // Handle to avoid unhandled rejection
console.log('Rejected promise:', rejectedPromise);

/**
 * EXAMPLE 3: .then() Chaining
 * Values flow through the chain
 */
console.log('\n--- Example 3: .then() Chaining ---');

Promise.resolve(5)
  .then((x) => {
    console.log('Step 1:', x);
    return x * 2;
  })
  .then((x) => {
    console.log('Step 2:', x);
    return x + 3;
  })
  .then((x) => {
    console.log('Step 3:', x);
    return x.toString();
  })
  .then((x) => {
    console.log('Final:', x, '(type:', typeof x + ')');
  });

/**
 * EXAMPLE 4: Returning Promises in Chain
 * Async operations in sequence
 */
console.log('\n--- Example 4: Returning Promises ---');

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

delay(50, 'First')
  .then((result) => {
    console.log(result);
    return delay(50, 'Second');
  })
  .then((result) => {
    console.log(result);
    return delay(50, 'Third');
  })
  .then((result) => {
    console.log(result);
  });

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

Promise.resolve('start')
  .then((value) => {
    console.log(value);
    throw new Error('Something went wrong!');
  })
  .then(() => {
    console.log('This will not run');
  })
  .catch((error) => {
    console.log('Caught error:', error.message);
    return 'recovered'; // Continue the chain
  })
  .then((value) => {
    console.log('Continued with:', value);
  });

/**
 * EXAMPLE 6: .finally() Method
 * Cleanup that always runs
 */
console.log('\n--- Example 6: .finally() ---');

function fetchData(shouldFail) {
  console.log('Loading...');

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldFail) {
        reject(new Error('Network error'));
      } else {
        resolve({ data: 'Success!' });
      }
    }, 100);
  });
}

fetchData(false)
  .then((data) => console.log('Data:', data))
  .catch((error) => console.error('Error:', error.message))
  .finally(() => console.log('Loading complete.'));

/**
 * EXAMPLE 7: Promise.all()
 * Wait for multiple promises
 */
console.log('\n--- Example 7: Promise.all() ---');

const promise1 = delay(100, 'Result 1');
const promise2 = delay(50, 'Result 2');
const promise3 = delay(75, 'Result 3');

Promise.all([promise1, promise2, promise3]).then((results) => {
  console.log('All results:', results);
  // Results are in order of input array, not completion order
});

/**
 * EXAMPLE 8: Promise.all() Failure
 * Fails fast if any promise rejects
 */
console.log('\n--- Example 8: Promise.all() Failure ---');

const successPromise = delay(100, 'Success');
const failPromise = new Promise((_, reject) => {
  setTimeout(() => reject(new Error('Failed!')), 50);
});

Promise.all([successPromise, failPromise])
  .then((results) => console.log('Results:', results))
  .catch((error) => console.log('Promise.all failed:', error.message));

/**
 * EXAMPLE 9: Promise.allSettled()
 * Wait for all, regardless of outcome
 */
console.log('\n--- Example 9: Promise.allSettled() ---');

const mixedPromises = [
  Promise.resolve('Success 1'),
  Promise.reject(new Error('Failure')),
  Promise.resolve('Success 2'),
];

Promise.allSettled(mixedPromises).then((results) => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`Promise ${index + 1}: fulfilled with "${result.value}"`);
    } else {
      console.log(
        `Promise ${index + 1}: rejected with "${result.reason.message}"`
      );
    }
  });
});

/**
 * EXAMPLE 10: Promise.race()
 * First promise to settle wins
 */
console.log('\n--- Example 10: Promise.race() ---');

const slow = delay(200, 'Slow');
const fast = delay(50, 'Fast');
const medium = delay(100, 'Medium');

Promise.race([slow, fast, medium]).then((winner) => {
  console.log('Race winner:', winner);
});

/**
 * EXAMPLE 11: Promise.any()
 * First fulfilled promise wins
 */
console.log('\n--- Example 11: Promise.any() ---');

const rejected1 = Promise.reject(new Error('Error 1'));
const rejected2 = Promise.reject(new Error('Error 2'));
const resolvedLater = delay(100, 'Finally resolved!');

Promise.any([rejected1, rejected2, resolvedLater])
  .then((result) => console.log('Any result:', result))
  .catch((error) => console.log('All rejected:', error));

/**
 * EXAMPLE 12: Timeout Pattern
 * Race between operation and timeout
 */
console.log('\n--- Example 12: Timeout Pattern ---');

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
  });
}

function fetchWithTimeout(ms) {
  const fetchPromise = delay(150, 'Data fetched!');
  return Promise.race([fetchPromise, timeout(ms)]);
}

// This will succeed (100ms fetch, 200ms timeout)
fetchWithTimeout(200)
  .then((result) => console.log('Fast enough:', result))
  .catch((error) => console.log('Too slow:', error.message));

// This will timeout (100ms fetch, 50ms timeout)
fetchWithTimeout(50)
  .then((result) => console.log('Fast enough:', result))
  .catch((error) => console.log('Too slow:', error.message));

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

const tasks = [
  () => delay(50, 'Task 1'),
  () => delay(50, 'Task 2'),
  () => delay(50, 'Task 3'),
];

// Sequential - 150ms total
async function runSequential() {
  const start = Date.now();
  const results = [];
  for (const task of tasks) {
    results.push(await task());
  }
  console.log(`Sequential: ${Date.now() - start}ms`, results);
}

// Parallel - 50ms total
async function runParallel() {
  const start = Date.now();
  const results = await Promise.all(tasks.map((t) => t()));
  console.log(`Parallel: ${Date.now() - start}ms`, results);
}

runSequential();
runParallel();

/**
 * EXAMPLE 14: Converting Callback to Promise
 */
console.log('\n--- Example 14: Callback to Promise ---');

// Callback-based function
function fetchDataCallback(id, callback) {
  setTimeout(() => {
    if (id < 0) {
      callback(new Error('Invalid ID'), null);
    } else {
      callback(null, { id, name: `User ${id}` });
    }
  }, 50);
}

// Promise wrapper
function fetchDataPromise(id) {
  return new Promise((resolve, reject) => {
    fetchDataCallback(id, (error, data) => {
      if (error) reject(error);
      else resolve(data);
    });
  });
}

// Generic promisify utility
function promisify(fn) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (error, result) => {
        if (error) reject(error);
        else resolve(result);
      });
    });
  };
}

const fetchPromisified = promisify(fetchDataCallback);

fetchPromisified(1).then((data) => console.log('Promisified:', data));

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

let attemptCount = 0;

function unreliableOperation() {
  return new Promise((resolve, reject) => {
    attemptCount++;
    console.log(`Attempt ${attemptCount}`);

    if (attemptCount < 3) {
      reject(new Error('Temporary failure'));
    } else {
      resolve('Success on attempt ' + attemptCount);
    }
  });
}

function retry(fn, attempts, delayMs) {
  return fn().catch((error) => {
    if (attempts <= 1) {
      throw error;
    }
    return delay(delayMs, null).then(() => retry(fn, attempts - 1, delayMs));
  });
}

retry(unreliableOperation, 5, 100)
  .then((result) => console.log('Retry result:', result))
  .catch((error) => console.log('All retries failed:', error.message));

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

function memoize(fn) {
  const cache = new Map();

  return function (key) {
    if (cache.has(key)) {
      console.log('Cache hit for:', key);
      return cache.get(key);
    }

    console.log('Cache miss for:', key);
    const promise = fn(key);
    cache.set(key, promise);
    return promise;
  };
}

const fetchUser = memoize((id) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, name: `User ${id}` }), 100);
  });
});

// First call - cache miss
fetchUser(1).then((user) => console.log('First:', user));

// Second call - cache hit
fetchUser(1).then((user) => console.log('Second:', user));

// Different key - cache miss
fetchUser(2).then((user) => console.log('Third:', user));

/**
 * EXAMPLE 17: Promise Queue
 * Process items with concurrency limit
 */
console.log('\n--- Example 17: Concurrency Limit ---');

class PromiseQueue {
  constructor(concurrency = 1) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }

  add(promiseFactory) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        factory: promiseFactory,
        resolve,
        reject,
      });
      this.process();
    });
  }

  process() {
    while (this.running < this.concurrency && this.queue.length > 0) {
      const { factory, resolve, reject } = this.queue.shift();
      this.running++;

      factory()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.running--;
          this.process();
        });
    }
  }
}

const queue = new PromiseQueue(2);

for (let i = 1; i <= 5; i++) {
  queue
    .add(() => {
      console.log(`Task ${i} started`);
      return delay(100, `Task ${i} result`);
    })
    .then((result) => console.log(`Task ${i} done:`, result));
}

/**
 * EXAMPLE 18: Promise.withResolvers()
 * New way to create promises (ES2024)
 */
console.log('\n--- Example 18: Deferred Pattern ---');

// Manual deferred pattern (before Promise.withResolvers)
function createDeferred() {
  let resolve, reject;
  const promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
}

const deferred = createDeferred();

// Resolve later
setTimeout(() => {
  deferred.resolve('Deferred value!');
}, 100);

deferred.promise.then((value) => console.log('Deferred:', value));

/**
 * EXAMPLE 19: Error Recovery
 */
console.log('\n--- Example 19: Error Recovery ---');

function fetchWithFallback(primary, fallback) {
  return primary.catch((error) => {
    console.log(`Primary failed: ${error.message}, trying fallback`);
    return fallback;
  });
}

const primaryServer = Promise.reject(new Error('Primary down'));
const fallbackServer = Promise.resolve('Fallback data');

fetchWithFallback(primaryServer, fallbackServer).then((data) =>
  console.log('Got:', data)
);

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

class APIClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
    this.cache = new Map();
  }

  // Simulated fetch
  fetch(endpoint, options = {}) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (endpoint.includes('error')) {
          reject(new Error(`Failed to fetch ${endpoint}`));
        } else {
          resolve({
            endpoint,
            data: { message: 'Success' },
            timestamp: Date.now(),
          });
        }
      }, 100);
    });
  }

  get(endpoint, { cache = false, timeout = 5000 } = {}) {
    const cacheKey = `GET:${endpoint}`;

    if (cache && this.cache.has(cacheKey)) {
      return Promise.resolve(this.cache.get(cacheKey));
    }

    const fetchPromise = this.fetch(endpoint);

    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Request timeout')), timeout);
    });

    return Promise.race([fetchPromise, timeoutPromise]).then((response) => {
      if (cache) {
        this.cache.set(cacheKey, response);
      }
      return response;
    });
  }

  getMultiple(endpoints) {
    return Promise.allSettled(endpoints.map((e) => this.get(e)));
  }
}

const api = new APIClient('https://api.example.com');

// Single request
api.get('/users').then((response) => console.log('Single:', response.endpoint));

// Multiple requests
api.getMultiple(['/users', '/posts', '/error-endpoint']).then((results) => {
  console.log(
    'Multiple:',
    results.map((r) =>
      r.status === 'fulfilled' ? r.value.endpoint : r.reason.message
    )
  );
});

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