Promises/A+ & Async await - JavaScript challenges

mitchell_cheng

Mitchell

Posted on November 4, 2024

Promises/A+ & Async await - JavaScript challenges

You can find all the code in this post at the repo Github.


Async programming Promises/A+ & Async await related challenges


Implement Promises/A+ with Promise.finally()

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((callbackFn) => callbackFn(this.value));
      }
    } 

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

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

  handlePromiseResult(result, resolve, reject) {
    if (result instanceof MyPromise) {
      result.then(resolve, reject);
    } else {
      resolve(result);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function'
      ? onFulfilled
      : (value) => value;
    onRejected = typeof onRejected === 'function'
      ? onRejected
      : (reason) => { throw reason; };

    // return a promise
    return new MyPromise((resolve, reject) => {
      const fulfilledHandler = () => {
        queueMicrotask(() => {
          try {
            const result = onFulfilled(this.value);
            this.handlePromiseResult(result, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      const rejectedHandler = () => {
        queueMicrotask(() => {
          try {
            const result = onRejected(this.reason);
            this.handlePromiseResult(result, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      if (this.state === 'fulfilled') {
        fulfilledHandler();
      } else if (this.state === 'rejected') {
        rejectedHandler();
      } else {
        this.onFulfilledCallbacks.push(fulfilledHandler);
        this.onRejectedCallbacks.push(rejectedHandler);
      }
    });
  }

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

  finally(onFinally) {
    if (typeof onFinally !== 'function') {
      return this.then();
    }

    return this.then(
      (value) => MyPromise.resolve((onFinally()).then(() => value)),
      (reason) => MyPromise.resolve(onFinally()).then(() => { throw reason })
    );
  }

  static resolve(value) {
    return new MyPromise((resolve) => resolve(value));
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }
}

// Usage example
const promise = MyPromise.resolve(1);
promise.then((value) => {
  console.log(value);
})
.then(() => {
  throw new Error('Error');
})
.catch((err) => {
  console.log(`Error catched: ${err}`);
});
Enter fullscreen mode Exit fullscreen mode

Implement async await with generator

function asyncGenerator(generatorFunc) {
  return function (...args) {
    const generator = generatorFunc(...args);

    return new Promise((resolve, reject) => {
      function handle(iteratorResult) {
        if (iteratorResult.done) {
          resolve(iteratorResult.value);
          return;
        }

        Promise.resolve(iteratorResult.value)
          .then(
            (value) => handle(generator.next(value)),
            (err) => handle(generator.throw(err)),
          );
      }

      try {
        handle(generator.next());
      } catch (err) {
        reject(err);
      }
    });
  }
}

// Usage example
function* fetchData() {
  const data1 = yield fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => res.json());
  console.log(data1);

  const data2 = yield fetch('https://jsonplaceholder.typicode.com/posts/2').then(res => res.json());
  console.log(data2);
}

// Create an async version of the generator function
const asyncFetchData = asyncGenerator(fetchData);

// Call the async function
asyncFetchData()
  .then(() => console.log('All data fetched!'))
  .catch(err => console.error('Error:', err));
Enter fullscreen mode Exit fullscreen mode

Reference

💖 💪 🙅 🚩
mitchell_cheng
Mitchell

Posted on November 4, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related