Learning Objectives

  • Master the Promise constructor syntax
  • Understand resolve and reject functions
  • Learn about the executor function
  • Create custom Promises for various scenarios

Promise Constructor Syntax

const promise = new Promise((resolve, reject) => {
  // Executor function body
});

Components:

  1. new Promise() - Creates a new Promise object
  2. Executor function - (resolve, reject) => { }
  3. resolve(value) - Fulfills the promise with a value
  4. reject(reason) - Rejects the promise with a reason

The Executor Function

The executor function runs immediately when the Promise is created:

console.log('Before');

const promise = new Promise((resolve, reject) => {
  console.log('Executor runs immediately!');
  resolve();
});

console.log('After');

// Output:
// Before
// Executor runs immediately!
// After

Key Points:

Using resolve()

Call resolve(value) when the async operation succeeds:

const successPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const data = { id: 1, name: 'Success' };
    resolve(data); // Fulfill with data
  }, 1000);
});

successPromise.then(data => {
  console.log('Received:', data);
});

What Can You Resolve With?

// Primitive values
resolve(42);
resolve('Hello');
resolve(true);

// Objects
resolve({ key: 'value' });
resolve([1, 2, 3]);

// Even another Promise!
resolve(anotherPromise);

// Undefined (no value)
resolve();

Using reject()

Call reject(reason) when the async operation fails:

const failurePromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('Something went wrong'));
  }, 1000);
});

failurePromise.catch(error => {
  console.error('Error:', error.message);
});

Best Practice: Always Reject with Error Objects

// ✅ Good - Error object with stack trace
reject(new Error('Database connection failed'));

// ❌ Bad - String (no stack trace)
reject('Database connection failed');

// ❌ Bad - Undefined
reject();

Common Patterns

1. Wrapping setTimeout

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

delay(2000).then(() => {
  console.log('2 seconds later');
});

2. Wrapping Callback-Based APIs

const fs = require('fs');

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (error, data) => {
      if (error) {
        reject(error);
      } else {
        resolve(data);
      }
    });
  });
}

readFilePromise('file.txt')
  .then(content => console.log(content))
  .catch(error => console.error(error));

3. Conditional Logic

function checkStock(itemId) {
  return new Promise((resolve, reject) => {
    const inStock = Math.random() > 0.5;
    
    setTimeout(() => {
      if (inStock) {
        resolve({ itemId, quantity: 10 });
      } else {
        reject(new Error('Out of stock'));
      }
    }, 1000);
  });
}

Important Rules

1. Call resolve() or reject() Only Once

new Promise((resolve, reject) => {
  resolve('First');
  resolve('Second'); // Ignored!
  reject('Error');   // Ignored!
});

The first call wins, all subsequent calls are ignored.

2. Executor Errors Auto-Reject

If the executor throws an error, the Promise is automatically rejected:

const promise = new Promise((resolve, reject) => {
  throw new Error('Oops!');
  // Equivalent to: reject(new Error('Oops!'));
});

promise.catch(error => {
  console.error(error.message); // 'Oops!'
});

Real-World Example: API Request Simulation

function fetchUser(userId) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching user ${userId}...`);
    
    setTimeout(() => {
      if (userId <= 0) {
        reject(new Error('Invalid user ID'));
        return;
      }
      
      const user = {
        id: userId,
        name: `User ${userId}`,
        email: `user${userId}@example.com`
      };
      
      resolve(user);
    }, 1500);
  });
}

fetchUser(5)
  .then(user => console.log('User:', user))
  .catch(error => console.error('Error:', error.message));

Key Takeaways

  • ✅ Use new Promise((resolve, reject) => {}) to create Promises
  • ✅ Executor function runs immediately
  • ✅ Call resolve(value) for success, reject(error) for failure
  • ✅ Only the first resolve/reject call matters
  • ✅ Always reject with Error objects
  • ✅ Executor errors auto-reject the Promise

Next Steps

Next, we'll learn how to consume Promises using .then(), .catch(), and .finally().