2

(new to nodejs, please be linient) I am trying to use an object-oriented approach to help me solve for using the output of async functions in the next set of promised calls. Right now I am facing an issue where the chain loses track of the base object, and sets the 'this' pointer to undefined.

I have been using this pattern extensively in golang, so I am used to this. Certainly, I am missing something important, but I can't figure out what.

Minimum code for issue replication

class Trial {
  constructor(name) {
    this.name = name
    //This is just an example of what I will want to be able to access.
    //Was planning to store the results of different functions so that I have
    //access to then in further chains (skipping 2 or 3 chanis)
  }
  async third() {
    console.log(`third ${this.name}`, ...arguments)
  }
  async second() {
    console.log(`second ${this.name} this is inside 2nd function`)
  }
  async first() {
    return this.second()
      .then(this.third)
  }
}

let t = new Trial('instance name')
t.first()

Output

second instance name this is inside 2nd function
Hint: hit control+c anytime to enter REPL.
/home/runner/TestVariadic/index.js:9
    console.log(`third ${this.name}`, ...arguments)
                              ^

TypeError: Cannot read properties of undefined (reading 'name')
    at third (/home/runner/TestVariadic/index.js:9:31)
Nitro
  • 1,063
  • 1
  • 7
  • 17
  • It's because of scoping of `this` within your third function when you call it like that. The value of `this` within functions can change depending on how you *call* the function. Either use an [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) like so: `.then(() => this.third())` which preserves `this` or explicitly bind `this` like so: `.then(this.third.bind(this))`. See more here: https://stackoverflow.com/questions/2236747/what-is-the-use-of-the-javascript-bind-method – nicholaswmin Dec 14 '22 at 04:28
  • Thanks that helped. Will post an answer that solved my problem for now. – Nitro Dec 14 '22 at 04:33
  • 1
    Don't. Find an S.O answer that answers this already and mark it as a duplicate. Or better yet delete this question. It's been answered numerous times. – nicholaswmin Dec 14 '22 at 04:34

1 Answers1

0

Found the answer I was looking for, and I am posting it as this was new learning for me, and was not completely obvious from what @Nicholaswmin explained.

Rather than just looking at how to solve the issue, I looked at the problem and realized something. I didn't need promises to do the thing I was trying to get done.

So I went for the following approach.

class Trial {
  constructor(name) {
    this.name = name
    //This is just an example of what I will want to be able to access.
    //Was planning to store the results of different functions so that I have
    //access to then in further chains (skipping 2 or 3 chanis)
  }
  async third() {
    console.log(`third ${this.name}`, ...arguments)
  }
  async second() {
    console.log(`second ${this.name} this is inside 2nd function`)
  }
  async first() {
    var callbacks = [this.second, this.third]
    var input = ''
    for(var idx in callbacks){
      var callback=callbacks[idx]
      input=callback(input)
    }
    return input
  }
}

let t = new Trial('instance name')
t.first()

But the output came like this.

Hint: hit control+c anytime to enter REPL.
/home/runner/Function-pointer/index.js:12
    console.log(`second ${this.name} this is inside 2nd function`)
                               ^

TypeError: Cannot read properties of undefined (reading 'name')
    at second (/home/runner/Function-pointer/index.js:12:32)
    at Trial.first (/home/runner/Function-pointer/index.js:19:13)
    at Object.<anonymous> (/home/runner/Function-pointer/index.js:26:3)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)

so then I tried @nicholaswmin's solution with arrow functions and it worked perfectly, both with promises and with the callbacks array.

And that's when lightbulbs started getting on in my head. Now I understand `how you call the function important, but based on what is common in all of these things is that, if you assign the function as a function pointer to a variable, (funnily enough assigning it to an array gives a 'promise' object to the 'this' pointer) this pointer becomes undefined.

Comeing to why my original solution had an issue in the "third" function, why my new solution had an issue in the "second" function and why the arrow function worked is

  1. When I used promises, I called the 'then' function which took my "third" function as its argument variable, and the "this" pointer became undefined.
  2. When I used the callback approach, I used the "callback" variable, and it did the same thing.
  3. The arrow pointer on the other hand is actually a shorthand as in ()=>{some work} is the same as function() {some work}. So here the "third" function never got stored in a variable and preserved its "this" pointer.

As for my original question of how to solve my problem, there were many ways, including the bind function and the arrow pointers.

@nicholaswmin: I know you asked me to mark the question as duplicate or even delete the question. Given that I have got 2 upvotes for my question most people have not found what they were looking for. This answer explains what went wrong with my approach, and something new(at least to me), unintuitive and interesting thing about js. I will defer to your opinion of what to do with the question.

Nitro
  • 1,063
  • 1
  • 7
  • 17