Promises

What is a Promise?

A Promise is an object representing the eventual completion or failure of an asynchronous operation. It's a cleaner alternative to callbacks for handling async code.

Try Promises

Code Editor
Console Output
Click "Run" to execute your code...

Example 1: Creating and Using Promises

// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
  const success = true;
  
  setTimeout(() => {
    if (success) {
      resolve('Operation successful!');
    } else {
      reject('Operation failed!');
    }
  }, 1000);
});

// Using the Promise
myPromise
  .then(result => {
    console.log(result); // Operation successful!
  })
  .catch(error => {
    console.error(error);
  });

// Promise states: pending, fulfilled, rejected
const promise1 = new Promise((resolve) => {
  setTimeout(() => resolve('Done!'), 1000);
});

console.log(promise1); // Promise { <pending> }

promise1.then(result => {
  console.log(result); // Done!
  console.log(promise1); // Promise { <fulfilled>: 'Done!' }
});

Example 2: Promise Chaining

// Chaining promises
function fetchUser(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: userId, name: 'John Doe' });
    }, 1000);
  });
}

function fetchPosts(user) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, title: 'Post 1', userId: user.id },
        { id: 2, title: 'Post 2', userId: user.id }
      ]);
    }, 1000);
  });
}

function fetchComments(posts) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { postId: posts[0].id, text: 'Great post!' },
        { postId: posts[1].id, text: 'Nice article!' }
      ]);
    }, 1000);
  });
}

// Clean promise chain
fetchUser(1)
  .then(user => {
    console.log('User:', user);
    return fetchPosts(user);
  })
  .then(posts => {
    console.log('Posts:', posts);
    return fetchComments(posts);
  })
  .then(comments => {
    console.log('Comments:', comments);
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('All operations complete!');
  });

Example 3: Promise.all() and Promise.race()

// Promise.all - waits for all promises to resolve
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) => {
  setTimeout(() => resolve('foo'), 1000);
});
const promise3 = new Promise((resolve) => {
  setTimeout(() => resolve('bar'), 500);
});

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // [3, 'foo', 'bar']
  });

// If any promise rejects, Promise.all rejects
const promises = [
  Promise.resolve(1),
  Promise.reject(new Error('Failed!')),
  Promise.resolve(3)
];

Promise.all(promises)
  .then(results => console.log(results))
  .catch(error => console.error('Error:', error.message)); // Error: Failed!

// Promise.race - resolves/rejects with first settled promise
const fast = new Promise((resolve) => {
  setTimeout(() => resolve('Fast'), 500);
});

const slow = new Promise((resolve) => {
  setTimeout(() => resolve('Slow'), 1000);
});

Promise.race([fast, slow])
  .then(result => console.log(result)); // Fast

// Promise.allSettled - waits for all, doesn't short-circuit
const mixed = [
  Promise.resolve(1),
  Promise.reject('Error'),
  Promise.resolve(3)
];

Promise.allSettled(mixed)
  .then(results => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: 'Error' },
    //   { status: 'fulfilled', value: 3 }
    // ]
  });

// Promise.any - resolves with first fulfilled promise
const p1 = Promise.reject('Error 1');
const p2 = new Promise((resolve) => setTimeout(() => resolve('Success'), 500));
const p3 = new Promise((resolve) => setTimeout(() => resolve('Also Success'), 1000));

Promise.any([p1, p2, p3])
  .then(result => console.log(result)); // Success

Example 4: Error Handling in Promises

// Basic error handling
const riskyOperation = new Promise((resolve, reject) => {
  const random = Math.random();
  
  if (random > 0.5) {
    resolve(`Success! Random: ${random}`);
  } else {
    reject(new Error(`Failed! Random: ${random}`));
  }
});

riskyOperation
  .then(result => console.log(result))
  .catch(error => console.error('Caught:', error.message));

// Multiple catch blocks
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('HTTP error');
    }
    return response.json();
  })
  .catch(error => {
    console.error('Network error:', error);
    throw error; // Re-throw to next catch
  })
  .then(data => {
    // Process data
    if (!data.valid) {
      throw new Error('Invalid data');
    }
    return data;
  })
  .catch(error => {
    console.error('Processing error:', error);
  });

// Error recovery
function fetchWithRetry(url, retries = 3) {
  return fetch(url)
    .catch(error => {
      if (retries > 0) {
        console.log(`Retrying... (${retries} attempts left)`);
        return fetchWithRetry(url, retries - 1);
      }
      throw error;
    });
}

// Always use .catch() or try/catch with async/await
// to prevent unhandled promise rejections

Example 5: Converting Callbacks to Promises

// Old callback style
function oldStyleAsync(value, callback) {
  setTimeout(() => {
    if (value > 0) {
      callback(null, value * 2);
    } else {
      callback(new Error('Value must be positive'), null);
    }
  }, 1000);
}

// Promisified version
function promisifiedAsync(value) {
  return new Promise((resolve, reject) => {
    oldStyleAsync(value, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

// Usage
promisifiedAsync(5)
  .then(result => console.log('Result:', result)) // Result: 10
  .catch(error => console.error('Error:', error));

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

const asyncFn = promisify(oldStyleAsync);
asyncFn(10)
  .then(result => console.log(result)) // 20
  .catch(error => console.error(error));

// Async/await with promises (modern approach)
async function modernAsync() {
  try {
    const result1 = await promisifiedAsync(5);
    console.log('Result 1:', result1);
    
    const result2 = await promisifiedAsync(result1);
    console.log('Result 2:', result2);
    
    return result2;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

modernAsync();

Key Points

  • Promises have three states: pending, fulfilled, rejected
  • Use .then() for success, .catch() for errors, .finally() for cleanup
  • Promise chains allow sequential async operations
  • Promise.all() runs promises in parallel, waits for all
  • Promise.race() resolves/rejects with first settled promise
  • Always handle errors to avoid unhandled rejections
  • Async/await is syntactic sugar over promises