javascript

    Metaprogramming

    02 Dec, 2023public

    Class in JS

    05 Dec, 2023public

    Promises

    13 Nov, 2023public

    Js notes

    11 Jan, 2024public

    Iteration in javascript

    15 Nov, 2023public
    economics

    Incentive

    21 Aug, 2023public
    demo

    Demo

    13 Nov, 2023public
    personal

    Teaching myself CS

    05 Dec, 2023public

    People

    12 Dec, 2023public

    Links

    25 Mar, 2024public
    computer graphics

    Computer graphics from scratch

    12 Dec, 2023public
    notag

    Flexbox practice

    14 Aug, 2024public
    Lying in wait, set to pounce on the blank page,are letters up to no good,clutches of clauses so subordinatethey'll never let her get away.
    - The Joy Of Writing, Wislawa Szymborska

    Promises

    .13 Nov, 2023

    Let's build Promise from scratch.

    My current understanding

    The use of promises is basically to add a function to javascript's task queue once a value has been obtained by a long-running process (that process is usually carried out in a different thread or other part of the os, like when we request some value over the network or from hard disk).

    let p = new Promise((resolve,reject) => {
      try {
        const value = //obtain some value from a long running task.
        resolve(value)
      } catch (e) {
        reject(e)
      }
    })
    

    Now p is container for the value we actually want. Later in our code, when we want to do something with that value we can do that like so:

    p.then((val) => {
      // do something with this value
    })
    

    what we've done above is basically we've added the function (given to .then as an argument) to javascript's task queue. The event loop will pick it up when it gets time and add it to the call stack to be executed.

    Breaking it down

    So a promise is container for a value. It's constructor takes in a function. That function gets two arguments - a resolve function and a reject function. A promise also has a state indicating whether it is pending, fulfilled (resolve has been called), or rejected (reject has been called).

    Each time we call .then on the promise we are adding a function to a list. When the promise is fulfilled we need to execute all the functions in that list.

    We'll implement the constructor first:

    class MyPromise {
      constructor(func) {
        this.value = undefined
        this.state = "pending"
        this.fulfillmentTasks = []
        this.rejectionTasks = []
        this.resolve = this.resolve.bind(this)
        this.reject= this.reject.bind(this)
        if (func) {
          
          func(this.resolve,this.reject)
        }
      }
      resolve(value) {
          if (this.state !== "pending") return this 
          this.state = "fulfilled"
          this.value = value
          this.rejectionTasks = []
          this.fulfillmentTasks.map(t => setTimeout(t, 0)) 
          this.fullfillmentTasks = []
          return this
        }
      reject(value) {
          if (this.state !== "pending") return this 
          this.state = "rejected"
          this.value = value
          this.fullfillmentTasks = []
          this.rejectionTasks.map(t => setTimeout(t, 0)) 
          this.rejectionTasks = []
          return this
        }
    }
    

    Implementing .then

    MyPromise.prototype.then = function(onFulfilled, onRejected) {
        switch (this.state) {
          case "pending": {
            this.fulfillmentTasks.push(() => onFulfilled(this.value))
            this.rejectionTasks.push(() => onRejected(this.value))  
            break
          }
          case "fulfilled": 
            setTimeout(() => onFulfilled(this.value), 0)
            break
          case "rejected":
            setTimeout(() => onRejected(this.value), 0)
            break
        }
    }
    
    
    const p = new MyPromise((res,rej) => {
      res(2)
    })
    
    p.then((val) => console.log(val))
    p.then((val) => console.log(val*2))
    p.then((val) => console.log(val*3))
    p.then((val) => console.log(val*4))
    
    

    Chaining .then callbacks:

    const p = new Promise((res,rej) => {
      res(2)
    })
    
    p.then((val) => val*2).then((val) => val*2).then((val) => val*2).then((val) => console.log(val))
    
    // should print 16
    

    For this we need to return a promise from .then function

    MyPromise.prototype.then = function(onFulfilled, onRejected) {
        const newPromise = new MyPromise()
    
        const fullfillmentTask = () => {
          const newValue = onFulfilled(this.value)
          newPromise.resolve(newValue)
        } 
        switch (this.state) {
          case "pending": {
            this.fulfillmentTasks.push(fullfillmentTask)
            this.rejectionTasks.push(() => onRejected(this.value))  
            break
          }
          case "fulfilled": 
            setTimeout(fullfillmentTask, 0)
            break
          case "rejected":
            setTimeout(() => onRejected(this.value), 0)
            break
        }
        return newPromise
    }
    
    
    const newp = new MyPromise((res,rej) => {
      res(2)
    })
    
    newp.then((val) => val*2).then((val) => val*2).then((val) => val*2).then((val) => console.log("val ->", val))
    
    module.exports.MyPromise = MyPromise
    

    Flattening the .then

    const p = new MyPromise((res) => {
      const p1 = new MyPromise((r) => r(2))
      res(p1)
    })
    
    p.then((val) => val.then(v =>  console.log(v)))
    //prints 2
    
    // But what we want is :
    p.then(val => console.log(val))
    //prints 2
    
    //so in effect p "becomes" p1
    

    To handle the above scenario:

    • p needs to "become" p1
    • p's value is p1's value
    • p's state is p1's state
    • we can't call p.resolve() once p1 resolves
    const imp = require("./main.js")
    const MyPromise = imp.MyPromise
    function isThenable(val) {
      return typeof val === "object" && val !== null && typeof val.then === "function"
    }
    
    MyPromise.prototype.fulfill = function(val) {
      if (isThenable(val)) return
      this.value = val
      this.state = "fulfilled"
      this.rejectionTasks = []
      this.fulfillmentTasks.map(t => setTimeout(t, 0)) 
      this.fullfillmentTasks = []
    }
    
    MyPromise.prototype.resolve = function(value) {
      if (this.settled) return this
      this.settled = true
    
      if (isThenable(value)) {
        value.then((v) => this.fulfill(v))
      } else {
        this.fulfill(value)
      }
      return this
    }
    
    
    //testing the flattening pattern.
    
    const p0 = new MyPromise((res) => {
      const p1 = new MyPromise((r) => r(2))
      res(p1)
    })
    
    p0.then(val => console.log("flattened val -> ", val))
    
    Table of contents
    My current understanding
    Breaking it down
    Chaining .then callbacks:
    Flattening the .then
    1let p = new Promise((resolve,reject) => {
    2  try {
    3    const value = //obtain some value from a long running task.
    4    resolve(value)
    5  } catch (e) {
    6    reject(e)
    7  }
    8})
    1p.then((val) => {
    2  // do something with this value
    3})
    1const p = new Promise((res,rej) => {
    2  res(2)
    3})
    4
    5p.then((val) => val*2).then((val) => val*2).then((val) => val*2).then((val) => console.log(val))
    6
    7// should print 16
    1const p = new MyPromise((res) => {
    2  const p1 = new MyPromise((r) => r(2))
    3  res(p1)
    4})
    5
    6p.then((val) => val.then(v =>  console.log(v)))
    7//prints 2
    8
    9// But what we want is :
    10p.then(val => console.log(val))
    11//prints 2
    12
    13//so in effect p "becomes" p1
    Abhimanyu