Jump to content
GreenSock

Dipscom

GSAP .call() usage

Go to solution Solved by GreenSock,

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I know the basic behaviour of the .call() method.

tl.call(myFunction);

function myFunction() {
 console.log("Works!");
}

What I would like to know is why when using a .call() on a object's method, it doesn't work:

tl1 = new TimelineLite();
tl2 = new TimelineLite();

// This does nothing
tl1.call(tl2.pause); 

// And this is wrong as the pause() is called immediately, regardless of the position of the call - As expected
tl1.call(tl2.pause());


// However, the bellow works

tl1.call(function() {
 tl2.pause();
}

Thanks people!

Link to comment
Share on other sites

  • Solution

That's just a scope issue. When you reference "tl2.pause", it's just pointing to that method itself and there's no inherent context for that (it's just how JavaScript works). In other words, tl2.pause is exactly the same as tl1.pause. Both point to exactly the same method on the prototype. 

 

The solution is simple, though - you can define the scope parameter. 

tl1.call(tl2.pause, null, tl2); //that last parameter is for scope. 

See the docs at http://greensock.com/docs/#/HTML5/GSAP/TimelineLite/call/

  • Like 2
Link to comment
Share on other sites

I see, I don't quite get it why but I'm sure it will come as I get more experience. It's enough to know that is to do with scope. And I get the solution you have provided.

 

Thank you.

Link to comment
Share on other sites

  • 2 years later...

@GreenSock you know what? Today I understood your reply on this topic. At last.

 

This is the crutial detail.

 

On 8/8/2016 at 3:10 PM, GreenSock said:

Both point to exactly the same method on the prototype

 

It took me two years and four days to understand what this line was saying. Today being August 12, 2018.

 

To anybody else that reads this thread and is feeling the struggle: Persevere. You will get it. One day. And you won't even notice when it clicks.

  • Like 2
  • Haha 3
Link to comment
Share on other sites

6 hours ago, Dipscom said:

Today I understood your reply on this topic. At last.

 

So what made it click?

 

It's a confusing concept. Maybe I can confuse you some more.

 

Let's create a simple person object. 

 

function Person(name) {
  this.name = name;
}

Person.prototype.logName = function() {
  console.log(this.name);
}

var dipscom = new Person("Pedro");

 

 

Call the logName method. Simple enough, and works as expected.

 

dipscom.logName(); // Pedro

 

 

Now look what happens if you try to call that method without dipscom 

 

timelineCall(dipscom.logName); // CodePen

function timelineCall(callback) {
  callback();
}

 

 

CodePen???  ? 

 

dipscom wasn't the caller of the logName method, so this now becomes the global object i.e. the window. It just happens that CodePen created a global name property with their name. In strict mode that would actually be an error because this would be undefined.

 

To get the correct behavior, you would need to call the method with call or apply

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

 

 

timelineCall(dipscom.logName, dipscom); // Pedro

function timelineCall(callback, scope) {
  callback.call(scope);
}

 

 

See the Pen OwqJQX?editors=1111 by osublake (@osublake) on CodePen

 

 

If you're curious, this is how GSAP handles callbacks. Animation is the base class for TweenLite/Max and TimelineLite/Max 

 

Animation.prototype._callback = function(type) {
  var v = this.vars,
    callback = v[type],
    params = v[type + "Params"],
    scope = v[type + "Scope"] || v.callbackScope || this,
    l = params ? params.length : 0;
  
  // speed optimization; call() is faster than apply() 
  // so use it when there are only a few parameters
  switch (l) { 
    case 0: callback.call(scope); break;
    case 1: callback.call(scope, params[0]); break;
    case 2: callback.call(scope, params[0], params[1]); break;
    default: callback.apply(scope, params);
  }
};

 

 

  • Like 4
Link to comment
Share on other sites

1 hour ago, OSUblake said:

So what made it click?

 

Being able to understand that tl2 is not the caller of the method but the prototype of tl2 is. And now with your more detailed example/explanation it's even more clear.

 

Always a pleasure to be educated here. :)

 

Link to comment
Share on other sites

Sounds like you got it! 

 

Going back to my example real quick, if the logName method wasn't on the prototype, then it would work correctly without having to use call.

 

function Person(name) {
  this.name = name;
  
  this.logName = () => {
    console.log(this.name);
  }
}

var dipscom = new Person("Pedro");

timelineCall(dipscom.logName); // Pedro

function timelineCall(callback) {
  callback();
}

 


However, without an arrow function you would have to save this to a variable. 

 

function Person(name) {
    
  this.name = name;
  
  // vm, self, _this, etc...
  var that = this;
  
  this.logName = function() {
    console.log(that.name);
  }
}

 

  • Like 4
Link to comment
Share on other sites

Uh! Uh! Uh!

 

I know this one! I do!

 

That is because arrow functions automatically bind the scope to their containing block. ?

 

Nice tip, by the way.

  • Like 3
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×