polyfill - PROMISE
Tags: javascript
Published: 2023-11-06 17:12:44

A polyfill is a piece of code that provides functionality that is not natively supported in a browser or environment. In this case, we'll create a basic polyfill for the Promise object in JavaScript. Promises are a part of the ES6 specification, so if you're working in an environment that doesn't support ES6 promises, you can use a polyfill to add support.

Here's a simple implementation of a Promise polyfill along with an explanation of the code:

```js
// Check if the Promise object is already defined in the environment
if (typeof Promise === "undefined") {
  // Define a constructor function for the Promise object
  function Promise(executor) {
    // Create internal state variables
    this.state = 'pending';
    this.value = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

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

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

    // Execute the executor function with resolve and reject as arguments
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // Define the `then` method
  Promise.prototype.then = function (onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      if (typeof onFulfilled === 'function') {
        return new Promise((resolve) => {
          resolve(onFulfilled(this.value));
        });
      } else {
        return new Promise((resolve) => {
          resolve(this.value);
        });
      }
    }

    if (this.state === 'rejected') {
      if (typeof onRejected === 'function') {
        return new Promise((resolve, reject) => {
          reject(onRejected(this.value));
        });
      } else {
        return new Promise((resolve, reject) => {
          reject(this.value);
        });
      }
    }

    if (this.state === 'pending') {
      return new Promise((resolve, reject) => {
        if (typeof onFulfilled === 'function') {
          this.onFulfilledCallbacks.push((value) => {
            try {
              resolve(onFulfilled(value));
            } catch (error) {
              reject(error);
            }
          });
        } else {
          this.onFulfilledCallbacks.push(resolve);
        }

        if (typeof onRejected === 'function') {
          this.onRejectedCallbacks.push((reason) => {
            try {
              resolve(onRejected(reason));
            } catch (error) {
              reject(error);
            }
          });
        } else {
          this.onRejectedCallbacks.push(reject);
        }
      });
    }
  };

  // Define the `catch` method
  Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
  };

  // Define the `resolve` and `reject` functions
  Promise.resolve = function (value) {
    return new Promise((resolve) => resolve(value));
  };

  Promise.reject = function (reason) {
    return new Promise((resolve, reject) => reject(reason));
  };

  // Define the `all` method
  Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
      const results = new Array(promises.length);
      let completedPromises = 0;

      promises.forEach((promise, index) => {
        promise.then((value) => {
          results[index] = value;
          completedPromises++;

          if (completedPromises === promises.length) {
            resolve(results);
          }
        }).catch(reject);
      });
    });
  };

  // Define the `race` method
  Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(resolve).catch(reject);
      });
    });
  };

  // Assign the polyfilled Promise object to the global scope
  window.Promise = Promise;
}

// Usage
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise resolved!");
  }, 1000);
});

myPromise.then((result) => {
  console.log(result); // Output: Promise resolved!
});

```

```js

function PromisePolyFill(executor) {
  let onResolve, onReject;
  let fulfilled = false,
    rejected = false,
    called = false,
    value;

  function resolve(v) {
    fulfilled = true;
    value = v;

    if (typeof onResolve === "function") {
      onResolve(value);
      called = true;
    }
  }

  function reject(reason) {
    rejected = true;
    value = reason;

    if (typeof onReject === "function") {
      onReject(value);
      called = true;
    }
  }

  this.then = function (callback) {
    onResolve = callback;

    if (fulfilled && !called) {
      called = true;
      onResolve(value);
    }
    return this;
  };

  this.catch = function (callback) {
    onReject = callback;

    if (rejected && !called) {
      called = true;
      onReject(value);
    }
    return this;
  };

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

PromisePolyFill.resolve = (val) =>
  new PromisePolyFill(function executor(resolve, _reject) {
    resolve(val);
  });

PromisePolyFill.reject = (reason) =>
  new PromisePolyFill(function executor(resolve, reject) {
    reject(reason);
  });

let np = new PromisePolyFill((resolve, reject)=>{
  setTimeout(()=>{
    resolve(2)
  }, 1000)
})

np.then((a)=>{
  console.log(a)
})
```