2

I'm trying to resolve this in a Promise and can't figure out why it's not working. An arrow function usually does it for me, but this is a bit more convoluted than usual.

The snippet below, for example, gives the following error:

TypeError: Cannot read property 'processResponse' of undefined

Where is this getting lost?

If the snippet below doesn't work, try this codepen.

class Page {
  init() {
    this.datatable = new Datatable();
    this.datatable.setBehavior(this.behaviorFlavor)
  }

  behaviorFlavor() {
    console.log('Doing action...');
    Ajax.get()
      // 'this' is undefined
      .then((data) => { this.processResponse(data) });
  }

  processResponse(data) {
    console.log('Processing response...');
  }
}

class Datatable {
  setBehavior(callback) {
    // Add event listener to rows, etc.
    $('button').click(() => {
      callback();
    });
  }
}

class Ajax {
  static get() {
    return new Promise(function(resolve, reject) {
      console.log("Sending request...");
      resolve();
    })
  }
}

(new Page).init();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div>
  <button>Click Here!</button>
  <p>Then check your browser's console to see the error.</p>
</div>
DonovanChan
  • 149
  • 2
  • 10
  • As much as the answers below (really, the same answer twice) are OK, I'd be more inclined to add a constructor to your `class Page` as follows `constructor() { this.behaviorFlavor = this.behaviorFlavor.bind(this);}` – Jaromanda X Jun 22 '18 at 02:26

2 Answers2

3

The this context is getting lost by passing the behaviorFlavor function reference as a callback. You're basically extracting the method from its class and attempting to execute it in isolation.

Try one of these alternatives...

  1. Use an arrow function to lock-in the this context

    setBehavior(() => { this.behaviorFlavor() })
    
  2. Directly bind the behaviorFlavor function

    setBehavior(this.behaviorFlavor.bind(this))
    
Phil
  • 157,677
  • 23
  • 242
  • 245
2

There is no calling context in your current code because the originator of the function call is just callback();. Try binding this.behaviorFlavor to this before passing it as the callback instead:

class Page {
  init() {
    this.datatable = new Datatable();
    this.datatable.setBehavior(this.behaviorFlavor.bind(this))
  }

  behaviorFlavor() {
    console.log('Doing action...');
    Ajax.get()
      // 'this' is undefined
      .then((data) => {
        this.processResponse(data)
      });
  }

  processResponse(data) {
    console.log('Processing response...');
  }
}

class Datatable {
  setBehavior(callback) {
    // Add event listener to rows, etc.
    $('button').click(() => {
      callback();
    });
  }
}

class Ajax {
  static get() {
    return new Promise(function(resolve, reject) {
      console.log("Sending request...");
      resolve();
    })
  }
}

(new Page).init();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div>
  <button>Click Here!</button>
  <p>Then check your browser's console to see the error.</p>
</div>
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320