TechnoCorner
TechnoCorner

Reputation: 5155

Implementing Simple Promise's finally method

I was trying to implement promise from scratch.

Question:

I wasn't sure how do I implement Finally? (I'm guessing finally will execute after then's and the catch is invoked. (ONLY once))

If you feel any refactors can be made to my code, please feel free to suggest. This is my naive attempt to implement promises.

This is my implementation:

function Promisify(fn) {
  let status = 0; // 0 = unfulfilled, 1 = resolved, 2 = rejected
  let result;
  let error;
  let thenFns = [];
  let catchFns = [];
  let finallyFn = undefined;

  // Public Methods.
  this.then = function(fn) {
    thenFns.push(fn);
    doThen();
    return this; // for chaining
  };
  this.catch = function(fn) {
    catchFns.push(fn);
    doCatch();
    return this; // for chaining
  };

  // TODO: Implement finally
  this.finally = function(fn) {
      finallyFn = fn;
    //   dofinally(fn);
      return this;
  }
  
  // Private Methods
  function resolve(r) {
    if (status) throw Error('can not resolve, already handled');
    status = 1;
    result = r;
    doThen();
  }

  function reject(e) {
    if (status) throw Error('can not reject, already handled');
    status = 2;
    error = e;
    doCatch();
  }

  function doThen() {
    if (status === 1) {
      while(thenFns.length) {
        thenFns.shift()(result);
      }
    }
  }

  function doCatch() {
    if (status === 2) {
      if (catchFns.length === 0) {
        console.error('uncaught error')
      }
      while(catchFns.length) {
        catchFns.shift()(error);
      }
    }
  }
  
  try {
    fn(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

// ========  QUESTION: Caller ================
const demoFail = new Promisify((resolve, reject) => {
    setTimeout(function() {
        reject('Howdy! from DemoFail')
    }, 1000);
});

demoFail
    .then(val => console.log("DemoFail Then!!"))
    .catch(console.error) //Should throw an error.
    .then(val => console.log("Second then!"))
    .catch(
    (err) => {
        throw new Error('error', err);
    })
    .finally(val => console.log("Executed Finally"))

Upvotes: 2

Views: 422

Answers (1)

jfriend00
jfriend00

Reputation: 707606

Here are some things missing from your implementation:

  1. .then() needs to return a new promise, not the same promise.
  2. The return value of a .then() handlers (if it's not a promise) becomes the resolved value of the newly returned promise returned in step #1.
  3. If the return value of a .then() handler is a promise, then the newly returned promise from step 1 doesn't resolve until the promise returned from the .then() handler resolves or rejects with that value.
  4. When calling a .then() handler, you need try/catch around the call to the handler because if the handler throws, then that turns the promise returned into step #1 into a rejected promise with the throw error as the reject reason.
  5. There is similar logic for all the .catch() handlers (it also returns a new promise and the return value of the handler affects the newly returned promise from step #1).
  6. When you store .then() or .catch() callback fns, you also need to store the newly create promise that you returned separate for each callback because that promise will be affected by the return value or exception thrown in the callback.

The spec itself that covers this is fairly simple. You can read it here. And, here's an implementation of that spec.

Your simple version looks like it probably works for one level of promise like fn().then(), but it doesn't do proper chaining and doesn't catch exceptions in handlers and doesn't pay attention to return values in handlers which are all pretty fundamental behaviors of promises. Unfortunately, there is no super simple way to write promises that includes the fundamental behaviors. It just takes more code to support them.

Upvotes: 1

Related Questions