Implement a Custom Promise Class

You are required to implement a custom Promise.

Promise is an object that represent the eventual completion or failure of an asynchronous operation. Promise can be in 3 states:

  • Pending: The initial state, neither fulfilled nor rejected.
  • Fulfilled: The operation completed successfully.
  • Rejected: The operation failed

Example

let promise = new Promise((resolve, reject) => {
  // Asynchronous operation
  let success = true; // Assume the operation succeeds

  if (success) {
    resolve("Operation successful!");
  } else {
    reject("Operation failed!");
  }
});

// Example of Usage
promise
  .then((result) => {
    console.log(result); // "Operation successful!"
  })
  .catch((error) => {
    console.error(error); // "Operation failed!"
  })
  .finally(() => {
    console.log("Operation completed."); // This runs no matter what
  });

Solution

General structure

const STATE = {
  PENDING: "PENDING",
  FULFILLED: "FULFILLED",
  REJECTED: "REJECTED",
};

class MyPromise {
  constructor(callback) {
    // Initial state of Promise is empty
    this.state = STATE.PENDING;
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      
    };

    const reject = (reason) => {
      
    };

    try {
      callback(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onSuccess, onFail) {
    // ...
  }

  catch(onFail) {
    // ...
  }

  finally(callback) {
    // ...
  }
}

resolve() and reject() methods

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach((callback) => callback(this.value));
      }
    };

    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach((callback) => callback(this.reason));
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
}

then() method

class MyPromise {

  constructor() {
    // ...
  }
  
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            resolve(onFulfilled(this.value));
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else if (this.state === "rejected") {
        setTimeout(() => {
          try {
            reject(onRejected(this.value));
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else {
        this.handlers.push({ onFulfilled, onRejected });
      }
    });
  }

}

catch() and finally() methods

class MyPromise {
  catch(onRejected) {
	return this.then(null, onRejected);
  }

  finally() {
    
  }
}

Summary

  • To implement a custom promise, you need to: Define the possible states (PENDING, FULFILLED, REJECTED).
  • Implement the resolve and reject methods to change the state and execute callbacks.
  • Implement the then, catch, and finally methods to register and handle callbacks.

References