How a value is being retrieved with partial function

Viewed 59

I have the following codes:

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class simplePromise {
  
  constructor(callback) {
    this.state = PENDING;
    this.value = null;
    // here so I can execute all the functions
    this.handlers = [];
    this.doResolve(callback, this.resolve.bind(this), this.reject.bind(this));
  }

  doResolve(callback, onResolved, onRejected) {
    let done = false;
    console.log(callback, 'callback')
    callback((val) => {
      console.log(val, 'val')
      if (done) return;
      done = true;
      onResolved(val)
    })
  }

  resolve(val) {
    this.state = FULFILLED;
    this.value = val;
    this.handlers.forEach(handler => handler);
  }

  reject(error) {
    this.state = REJECTED;
    this.value = error;
    this.handlers.forEach(handler => handler);
  }
}


const promiseA = new simplePromise( (resolutionFunc, rejectionFunc) => {
  resolutionFunc(777);
});

What puzzles me is the following lines:

callback((val) => {
      console.log(val, 'val')

The value of val is correctly 777. However, I don't understand why. According to my understanding, I originally pass in a callback function with 2 arguments, so I thought val would have the value of

(resolutionFunc, rejectionFunc) => {
  resolutionFunc(777);
}

How does it correctly figure out the value in this case to be 777?

1 Answers

That is a real good question... And I it takes nerves to attempt a clear answer. I fear having to edit 400 times to improve that first quick draft. Hopefully, it will remain a draft, but will be enought to jump in some more in-dept tutorials after.


First, that really is a simplePromise, since as is, it just cannot be rejected.

You correctly identified what is here called callback... That is the function passed to the simplePromise instance.

Now notice that in the promise constructor, there is a function call:

this.doResolve(callback, this.resolve.bind(this), this.reject.bind(this));

where the callback is passed as the 1st argument and the other argument respectively are the resolve and reject functions defined as methods of the class. doResolve executes right away at promise instanciation.

So executing doResolve, the callback is called... Again passing it a function as first argument:

(val) => {
  console.log(val, 'val')
  if (done) return;
  done = true;
  onResolved(val)
}

That function in fact, is the resolutionFunc argument of the callback that was provided to the promise!

So fun... So fun...

Stay with me! So we still are at the callback execution... And we call resolutionFunc with 777 as argument. We could have passed in a rejectionFunc function too.

And that is where we finally execute:

(val) => {
  console.log(val, 'val')
  if (done) return;
  done = true;
  onResolved(val)
}

That's why console logging val outputs 777... It never went anywhere else. It was directly passed from resolutionFunc(777).

So Is it is just a bunch functions passed as a function to call themselves? In short?

Yes... But there is an advantage to it. The advantage of the promise "structure" is to have a whole set of way more complex functions, that would be repetitive otherwize, in a class and pass in just the "real use-case" changing part (the callback).

It allows to have a repetitive mecanism out of the way from the rest of the logic when programming.

There also is an object binded to the instance, notably the state which could be used by .done(), .fail() etc... So some more complex logic can be chained using the object.

What is missing in this too simple promise... Is a really doing something aside just returning the provided value untouched. It could be a check if it is a number... Or server response check, if you think about Ajax for example.


Below, I just added some console logs with numbers... So you can notice the order of execution. ;)

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class simplePromise {
  
  constructor(callback) {
    this.state = PENDING;
    this.value = null;
    // here so I can execute all the functions
    this.handlers = [];
    this.doResolve(callback, this.resolve.bind(this), this.reject.bind(this));
  }

  doResolve(callback, onResolved, onRejected) {
    console.log("1")
    console.log(callback)
    let done = false;
    callback((val) => {
      console.log("2")
      if (done) return;
      done = true;
      onResolved(val)
    })
  }

  resolve(val) {
    console.log("3")
    this.state = FULFILLED;
    this.value = val;
    this.handlers.forEach(handler => handler);
  }

  reject(error) {
    console.log("4")
    this.state = REJECTED;
    this.value = error;
    this.handlers.forEach(handler => handler);
  }
}


const promiseA = new simplePromise( (resolutionFunc, rejectionFunc) => {
  console.log("5")
  console.log(resolutionFunc)
  resolutionFunc(777);
});


Why 5 is logged before 2?

Because console.log("2") is inside a function statement that is passed as an argument. And that function is called inside callback right after console.log("5")

resolutionFunc is:

(val) => {
  console.log("2")
  if (done) return;
  done = true;
  onResolved(val)
}

The key is to notice the when a function statement is getting called. I added two console logs to the snippet... hoping to make it obvious.

Related