Your Universal Remote Control Center
RemoteCentral.com
Philips Pronto Professional Forum - View Post
Previous section Next section Up level
Up level
The following page was printed from RemoteCentral.com:

Login:
Pass:
 
 

Topic:
Inside system callbacks, keyword 'this' might not be what you expect.
This thread has 3 replies. Displaying all posts.
Post 1 made on Thursday May 19, 2011 at 22:56
mlorenzen
Lurking Member
Joined:
Posts:
January 2009
2
I've been trying to debug some errors I was getting with a ProntoScript object I was fiddling with, and I boiled it down to this:

function Device() {
this.sendCommand = function(s) {
System.print("command: " + s);
};

this.pollPower = function() {
this.sendCommand("pollPower");
};
}

var proj = new Device();
scheduleAfter(10, proj.pollPower);


This produces:

TypeError: this.sendCommand is not a function

If I change the this.pollPower method to call proj.sendCommand, everything works.

I also directly copied the library example from the philips doc (the SampleLib timer) and got similar errors. Any ideas what I'm doing wrong here?

This is with ProntoEdit 2.4.23, and I get the same results ont he remote or in the simulator.

-mike

Last edited by mlorenzen on May 21, 2011 15:28.
Post 2 made on Friday May 20, 2011 at 21:44
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,996
Below will explain why the use of 'this' inside closure functions intended for system-level callbacks does contain the value of the outer object as one might think.

The value of 'this' depends on how you are calling the function including the context in which the function is being called (system callback). Note that in serial and socket callbacks, the keyword 'this' is set up to be the serial port or socket that is issuing the callback. In the case of scheduleAfter callbacks, 'this' is not your 'Device' instance, but rather the Activity instance. i.e. It will be the value of CF.activity().

If you were to change the code to:

var proj = new Device();
proj.pollPower();

You'd see the result you expect. However, your closure function is called from a different context when scheduleAfter() is used.


To overcome this, you can make one very small tweak to solve your issue.

function Device()
{
// you should define the object instance in a variable that can be referenced throughout other functions and not use 'this' keyword.
var target = this;
this.sendCommand = function(s)
{
System.print("command: " + s);
};

this.pollPower = function()
{
// note that we do not use 'this' here because the function, even though it is a member field of your 'Device' instance, is still called as a non-object-based JS function and 'this' will be the CF.activity() scope.
target.sendCommand("pollPower");
};
}

var proj = new Device();
scheduleAfter(10, proj.pollPower);

The above code, after 10 or so miliseconds should issue the System.print as you expect.

Now, you can resort to something else as well and allows you to define the function any place (as standalone function outide of Device, if you like).
What you want to do is to pass value of 'proj' as a single parameter to be used with the call to the function at a later time as in:

function Device()
{
this.sendCommand = function(s)
{
System.print("command: " + s);
};

this.pollPower = function(whichDevice)
{
// note that we do not use 'this' here because the function, even though it is a member field of your 'Device' instance, is still called as a non-object-based JS function and 'this' will be the CF.activity() scope.
whichDevice.sendCommand("pollPower");
};
}

var proj = new Device();
scheduleAfter(10, proj.pollPower, proj);

Note that when the pollPower function is called, it will receive this single parameter which you use as a substitution for 'this'.

Hopefully now, you can see why scheduleAfter function was designed to support that 3rd parameter. It specifically was added to help overcome issues with 'this' and scope changes if a function is called via a callback vs through a member field.

Background on why things are the way they are!!!

Note that with this line:

scheduleAfter(10, proj.pollPower);

you are simply providing the function to be called, (proj.pollPower) and not the scope in which is should be called.

The behind-the-scenes implementation of scheduleAfter use the equivalent of the Javascript Function.call or Function.exec methods to call your function.

If you look at a Javascript reference, you will see that either of these functions requires that the first parameter be the 'scope' to which the call applies. In the case of a TCPSocket, UDPSocket, Serial, Input, Activity, or a Widget, the value supplied for the first parameter is the ProntoScript object for which the callback applies.

My final note on this subject is a reminder that when the repeat interval of a Page or a Widget is specified and causes script to be executed, the keyword 'this' is the value of the Prontoscript object for which your code is applied (with the values being those returned by a call to CF.page() or CF.widget()/GUI.widget() respectively).

If you still need more information or would like to read more about this topic, you can see both the chapter/section in the Dev Guide on scopes and also see the Javascript book by David Flanagan.


If you don't mind, would you please consider editing the above post so that the subject line reads something like "Inside system callbacks, keyword 'this' might not be what you expect."

Best of luck.

Lyndel

Last edited by Lyndel McGee on May 20, 2011 21:53.
Lyndel McGee
Philips Pronto Addict/Beta Tester
OP | Post 3 made on Saturday May 21, 2011 at 15:37
mlorenzen
Lurking Member
Joined:
Posts:
January 2009
2
Thanks Lyndel, that did the trick.

It is similar to the way python works with class methods. When you define, it the first param is always "this", but the calls to that method are implicitly called with the class instance as the first param to the method.

I didn't realize that the scheduleAfter was calling the class method in this way.

And I found that the reason my implementation of the Philips sample library didn't work, was a typo that I just couldn't see before. Sometimes you just need to step away from these things for a bit.

Thanks again.
-m
Post 4 made on Saturday May 21, 2011 at 23:53
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,996
Y/W. Thank you for editing the title as well. I hope I did an OK job of explaining which scope is active for 'this' during callback.

I often use JSLint to find syntax errors and typos. Another 'gotcha' I often run into is a reference to variables or fields where I specify the wrong casing. Note that JSLint will NOT catch this type of error.

For example:

var o = {};
o.myField = 123;
System.print(o.MyField);
Lyndel McGee
Philips Pronto Addict/Beta Tester


Jump to


Protected Feature Before you can reply to a message...
You must first register for a Remote Central user account - it's fast and free! Or, if you already have an account, please login now.

Please read the following: Unsolicited commercial advertisements are absolutely not permitted on this forum. Other private buy & sell messages should be posted to our Marketplace. For information on how to advertise your service or product click here. Remote Central reserves the right to remove or modify any post that is deemed inappropriate.

Hosting Services by ipHouse