Question 81

Question

Describe how to implement a basic Promise polyfill.

Answer

Basic Polyfill Implementation:

if (!Promise) {
  window.Promise = function(executor) {
    if (typeof executor !== 'function') {
      throw new TypeError('Executor must be a function');
    }
    let state = 'pending';
    let value;
    let resolveQueue = [];
    let rejectQueue = [];

    const resolve = (v) => {
      if (state === 'pending') {
        value = v;
        state = 'fulfilled';
        processQueue(resolveQueue);
      }
    };

    const reject = (r) => {
      if (state === 'pending') {
        value = r;
        state = 'rejected';
        processQueue(rejectQueue);
      }
    };

    executor(resolve, reject);

    const processQueue = (queue) => {
      while (queue.length > 0) {
        const callback = queue.shift();
        try {
          callback(value);
        } catch (error) {
          // Handle errors appropriately here
        }
      }
    };

    return {
      then: function(onFulfilled, onRejected) {
        if (typeof onFulfilled !== 'function') {
          onFulfilled = value => value; 
        }
        if (typeof onRejected !== 'function') {
          onRejected = error => { throw error };
        }
        return new Promise((resolve, reject) => {
          if (state === 'fulfilled') {
            try {
              resolve(onFulfilled(value)); 
            } catch (error) {
              reject(error);
            }
          } else if (state === 'rejected') {
            try {
              resolve(onRejected(value));
            } catch (error) {
              reject(error);
            }
          } else { 
            resolveQueue.push(() => {
              try {
                resolve(onFulfilled(value));
              } catch (error) {
                reject(error);
              }
            });
            rejectQueue.push(() => {
              try {
                resolve(onRejected(value));
              } catch (error) {
                reject(error);
              }
            }); 
          }
        });
      },
    };
  };
}

Explanation:

  1. Check for Native Promises: The if (!Promise) block ensures the polyfill is only executed in environments without native Promise support.

  2. Custom Promise Constructor: We create our own Promise constructor function, mimicking the behavior of the built-in one.

  3. States (pending, fulfilled, rejected): The Promise can exist in these three states to track its execution status.

  4. Queues (resolveQueue, rejectQueue): These queues store callbacks that should be executed when the Promise is fulfilled or rejected. This allows us to handle asynchronous operations without blocking the main thread.

  5. resolve() and reject() Functions: These functions update the state of the Promise and trigger the execution of callbacks in the corresponding queue.

  6. then() Method: The polyfill's then() method accepts two optional callback functions: one for success (onFulfilled) and one for failure (onRejected). It returns a new Promise, which allows chaining asynchronous operations.

Important Notes:

  • This is a very basic polyfill. For more comprehensive coverage, consider using existing well-maintained polyfills like the one from core-js:

    import 'core-js/stable'; // Includes Promises polyfill and other essential features

Last updated