Unpacking JavaScript 3: Async Code - Method Chaining

Sk - Sep 5 '22 - - Dev Community

This article will not introduce Async code yet, but sequential programming, which is actually a good segue to promises and async code.

for us a sequential program is as easy as it sounds, a program with components that execute in sequence based on a previous component in the sequence(chain)

e.g String operations in JS



"Hello World".split("").reverse().join("")

  // split reverse and join will execute in sequence based on the
  // previous component's(object) result
Enter fullscreen mode Exit fullscreen mode

This sequence or chain formed by these functions is formally known as method chaining.

Method Chaining(MC)

a simple, even not complete explanation of MC to build from would be: passing down an object from method to method in a chain

let's think about objects a bit

Dot operations and Objects

Dot Op examples


.forEach()
.toString()
.call()
.bind()


Enter fullscreen mode Exit fullscreen mode

A dot operation accesses a property of an object.


const simpleObject = {
  name: "sk"

}



console.log(simpleObject.name)  // sk  - produce by a simple dot op 

Enter fullscreen mode Exit fullscreen mode

Dot op's assumption in JS is fairly simple: Dot op assumes whatever is being operated on is an object(whether during runtime or declaration), the right side of the dot assumes the left is an object

let str = new String("hello")

let arr = new Array()



console.log(typeof str)  // Object 

console.log(typeof arr)  // Object

str.split()
arr.forEach()

// fun fact most primitives in JS are turned to objects during
// runtime, hence it's possible to dot operate on a primitive 
// e.g string  "Hello world".split() 

Enter fullscreen mode Exit fullscreen mode

what's the point of all this?

 yAxisG

 .append("text")

 .attr('class', 'axis-label')

 .attr('y', -80)

 .attr('x', -innerHeight / 2)

 .attr('transform', `rotate(-90)`)

 .attr('fill', 'black')

 .attr('text-anchor', 'middle')

 .text(YaxisLabel)



Enter fullscreen mode Exit fullscreen mode

what you looking at is a snippet of D3, a JS visualization library, chaining multiple dot operations, we said the right side of dot ops assume the left is an object.

let's look at append from D3

append("text").attr("class", "axis-label")

// attr assumes append is an object, not only an object, but one 
// that has a property attr, which is a function

// this is what attr assumes 

const append = {
    attr: function(arg1, arg2){

        // do some attr stuff
    }

}

append('text').attr()

// however if you observe carefully append is not an object, 
// but a function("text") taking a text arg ,
// so how is this possible, simple 
// append returns an object with the method attr 
// and that is what method chaining is: passing down an object //from method to method in the chain
// will make sense as we implement it


Enter fullscreen mode Exit fullscreen mode

append returns an object with the attr method, attr returns an object with the attr and text method and so on,
but we do not have to define the shape of the passed objects by "hand", we have OOP for that, we know that the this keyword refers to the context around it, if called inside an object this refers to that object, we can achieve method chaining by having a bunch of methods in an object, each function after doing it's job, returns this, which refers to the current object

const chainable = {

     one : function() {

     console.log("one")

     return this;    // this === chainable object

     }, 


     two: function () {

     console.log("two")

     return this



     },

     three: function() {

     console.log("three")

       return this;

     }





}




chainable.one()

.one()

.one()

.two()

.three()

.one()

// you can create a huge sequence as you can see above, each 
//method will return chainable
// to take it a step further you can even encode state in chainable and have it updated as you go along
// it's very easy

Enter fullscreen mode Exit fullscreen mode

each method in chainable returns well: the chainable object, each dot operation in the chain expects the previous one to return an object, which defines the accessed property for example

chainable 
.one()  // expects chainable to have method one 
.two() // expects one() to have method two(), which does not but returns an object that does

// and so on


Enter fullscreen mode Exit fullscreen mode

you can achieve this with classes also.


class Axis{


    append(text){
        // do something with text 
        console.log(text)
        return this; // current instance object 


    }

    attr(key, property){
      // do something 
      console.log(key, property)

      return this 

    }

    text(text){
     // do something 
     console.log(text)

     return this 

    }


}


const yAxis = new Axis()

yAxis
.append("hello world")
.attr("zIndex", 1)
.attr("backgroundColor", "black")
.text("hello again")
.text("good bye")
.attr("display", "flex")





Enter fullscreen mode Exit fullscreen mode

With that we achieve sequential programming, doing an operation based on the previous one, this is a good Segue to promises, as this is what promises are (kind of), methods that return objects called promises, these objects define then, catch and finally methods(sound familiar?)

next we will introduce promises

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .