-
• #2
An interesting turn takes this code - which - in a nutshell:
- defines the same structure but with use of constructor and prototype
- creates first an obj with id = "objx"
- creates second an object with id = "obj"
invokes
.meth()
onobj
// .bind() not working? function Obj(id) { this.id = id; this.cnt = 0; } Obj.prototype.meth = function() { console.log("-----.meth()-----"); console.log(this); console.log("id=" + this.id); if ("obj" == this.id) { console.log("'this' is 'obj':"); console.log("id="+ this.id + " in invocation # " + (++this.cnt)); if (this.cnt < 3) { var _t = this; setTimeout(_t.meth,1); } } else { console.log("'this' is (unexpectedly) NOT 'obj'"); } console.log("-----/.meth()-----"); }; var objx = new Obj("objx"); var obj = new Obj("obj"); obj.meth.bind(obj); function onInit() { obj.meth(); }
The output for the bound this produced with
console.log(this)
must have somthing to do with the constructor, becauase all - both - instancesobjx
andobj
are listed:|_| http://espruino.com 1v81 Copyright 2015 G.Williams >echo(0); =undefined >onInit(); -----.meth()----- { "id": "obj", "cnt": 0 } id=obj 'this' is 'obj': id=obj in invocation # 1 -----/.meth()----- =undefined -----.meth()----- { "Obj": function (id) { this.id = id; this.cnt = 0; }, "objx": { "id": "objx", "cnt": 0 }, "obj": { "id": "obj", "cnt": 1 }, "onInit": function () { obj.meth(); }, "console": function () { [native code] } } id=undefined 'this' is (unexpectedly) NOT 'obj' -----/.meth()----- >
- defines the same structure but with use of constructor and prototype
-
• #3
Did you try this on desktop JavaScript? I just did with jsbin and it behaves exactly the same way in your first example.
I think your problem is that you're not using
bind
correctly.bind
returns a bound function, but doesn't alter the one you pass as an argument.So:
a.bind(b); // does nothing a = a.bind(b); // does what you want
-
• #4
Ic - ...dddooooh.. :(). Therefore, the proper usage is (see line
15
);// .bind() working this way: var obj = { id:"obj" , cnt: 0 , meth:function() { if ("obj" == this.id) { console.log("'this' is 'obj':"); console.log("id="+ this.id + " in invocation # " + (++this.cnt)); if (this.cnt < 3) { setTimeout(this.meth,1); } // <--- 'OK' } else { console.log("'this' is (unexpectedly) NOT 'obj'"); } } }; obj.meth = obj.meth.bind(obj); // <--- adjusted function onInit() { obj.meth(); }
And the output confirms that
.bind()
works when 'properly' used:|_| http://espruino.com 1v81 Copyright 2015 G.Williams >echo(0); =undefined >onInit() 'this' is 'obj': id=obj in invocation # 1 =undefined 'this' is 'obj': id=obj in invocation # 2 'this' is 'obj': id=obj in invocation # 3 >
Alternative - to avoid the awkward redefinition / reassignment - and for the case of multiple intances, it is better to adjust line
10
as follows and to drop line16
altogether:if (this.cnt < 3) { setTimeout(this.meth.bind(this),1); } // <--- 'BETTER'
Drawback of this cleaner solution is the recreation of the bound function everytime... To avoid this, the bound function could be assigned as separate
.methBound
property and reused this way:... if (this.cnt < 3) { setTimeout(this.methBound,1); } // <--- 'OK' ... ... obj.methBound = obj.meth.bind(obj); // <--- bind once ...
-
• #5
I could not help but to read your post with s/obj/crystal/g and found it kind of funny.
crystal.meth.bind(crystal);
is the way to go...?Got it.
-
• #6
...yep, the abstraction and reduction to the minimum made it look like this... Actual code is this code below and hardware as attached pic... and it works pretty neat... it is a way to get out of callback hell and reduce it to sequence hell... I do not know what is better... global controller
gCtl
s the 'drug' to connect all kinds of (different types of) 'crystals' with meth(ods)... in correct sequence and with set forth configuration(s)... LOL// /ESP8266/xyz/abc.js // g(lobal)Log(ging) function: var gLog = function(v) { console.log(v); }; // g(lobal) C(on)t(ro)l(ler) that handles: // - sequencing of initializers // - run workers after save() and power-cycle / power-on reset var gCtl = { is:[], i:-1 // (registered) i(initializer)s, index for iterating initializers , adi: function(i) { this.is.push(i); } // add/register initializer , ini: function(ok,str) { if (this.i >= 0) { // invoke registered initializers gLog("i[" + this.i + "]" + ((str)?str:"") + ": " + ((ok)?"ok":"failed")); } if (ok) { if (++this.i < this.is.length) { var _t = this; setTimeout(function(){_t.is[_t.i](function(ok,txt){_t.ini(ok,txt);});},1);} } else { /* is[i]() failed */ /* code to handle failures... may be retries... not built/used yet */ } } , run: function() { this.i = -1; this.ini(true); } // run() invoked in onInit() }; /* * CONFIGS AND MODULES */ // g(lobal)D(isplay) and its DSP_C(onfiguration) objects var gD = null, DSP_C = { md:require("ILI9341") , spi:SPI1 , spiC: {sck:A5, miso:A6, mosi:A7, baud:1000000} , dc:B10, cs:B1, rst:B13 }; // g(lobal) T(e)rm(inal) and its T_C(onfiguration) objs using gD(isplay) and var gTrm = null, T_C = {w:240, h:320, lh:12}; gTrm = { c:T_C , cl: 0 , p: function(l,nl) { var c = this.c; if (this.cl + c.lh > c.h) { this.cl = 0; } gD.setColor(0,0,0); gD.fillRect(0,c.cl,c.w-1,c.l + (c.lh*2) - 3); gD.setColor(1,1,1); gD.drawString(l,0,this.cl); if (nl) { this.cl += c.lh; } } }; // http and its HTTP_C(onfiguration) objs - mainly list of urls var http = null, HTTP_C = { md:require("http") , urls: [ "http://www.pur3.co.uk/hello.txt" , "http://weather.noaa.gov/pub/data/forecasts/marine/coastal/pz/pzz535.txt" ] }; // g(lobal) W(ifi) and its WIFI_C(onfiguration) objects var gW = null, WIFI_C = { md:require("ESP8266WiFi_0v25") , ser:Serial2, bd:115200, serC:{tx:A2, rx:A3} , ssid:"myWLAN", lak:"myWLANpassword" , ip:"", p:80 }; /* * INITIALIZERS */ // display initializer with callback 'cb' to return control to gCtl sequencer // does display setup and connect spi to it as set forth in configuration and // clears display gCtl.adi(function(cb){ var c = DSP_C; c.spi.setup(c.spiC); gD = c.md.connect(c.spi, c.dc, c.cs, c.rst, function(){ gD.clear(); cb(true,"display"); }); } ); // wifi initializer 1 (create wifi) with callback 'cb' to return control to gCtl sequencer // does display setup and connect serial to it as set forth in configuration and // creates g(lobal)W(ifi) object gCtl.adi(function(cb){ var c = WIFI_C; c.ser.setup(c.bd, c.serC); gW = c.md.connect(c.ser, function(err){ if (err) { cb(false,"wifi.reset: " + err); } else { cb(true,"wifi.reset"); } }); } ); // wifi initializer 1 with callback 'cb' to return control to gCtl sequencer // does display setup and connect via spi to it as set forth in configuration gCtl.adi(function(cb){ var c = WIFI_C; gW.connect(c.ssid, c.lak, function(err){ if (err) { cb(false,"wifi.connect: " + err); } else { cb(true,"wifi.connect"); } }); } ); /* * WORKERS */ function getb(udx) { var c = HTTP_C; gLog(c.urls[udx] + ":"); c.md.get(c.urls[udx], function(res){ res.on('data', function(dta){ // gLog(dta); gTrm.p(dta,true); }); }); } function getm(udx) { var c = HTTP_C; gLog("---> " + c.urls[udx] + ":"); var d = true, l = "", ol, nl; c.md.get(c.urls[udx], function(res){ res.on('data', function(dta){ l = l + dta; var ls = l.split("\n"), lm = ls.length - 1, lx = -1; while (++lx < lm) { ol = (l = ls[lx]).length; if ((d = d && (l != "$$"))) { while (ol > (nl = (l = l.replace(" OR LESS","|<")).length)) ol = nl; while (ol > (nl = (l = l.replace(" TO ","~")).length)) ol = nl; while (ol > (nl = (l = l.replace(" TO","~")).length)) ol = nl; gTrm.p(l,true); } } l = ls[lm]; ls = null; }); }); } function onInit() { gCtl.run(); }
gCtl is global Control for initializers... and the discussed line is line #
15
, which - in this case uses nested anonymous functions... (Above code does include application main flow/control yet... it is next: for example to store the info/forecast and go to a deep sleep until next user action to show stored info or info/forecast expires - zoom in on pic below to read what is displayed... gTerm - terminal - is not working correct yet: override / scroll...). setTimeout() is used to break the invocation chaining / avoid nested calling / to do stack 'reset' (to give Espruino a memory relieve...)The global controller for initializations is used to separate configuration from initialization - first - and - second - to have reliable constructs to call from
onInit()
afer saving the code and re-powering the device disconnected from Web IDE. An earlier approach was the Tasker - Task synchronizer - kind of a 'purposed' Promise. Another serialization approach has been discussed/suggested in conversation [How to carry out functions in a sequential manner]. (http://forum.espruino.com/conversations/276388).Latter allowed to put calls like commands into a fifo and execution has to await an ack before moving on to the next command... almost the same as making a callback.
Log output of above initializer produced in line
13
looks like:i[0] display: ok i[1] wifi.reset: ok i[2] wifi.connect: failed
In case of failed, above - very lean - initializer control has no retries or error handling (vs the not so lean Tasker has).
1 Attachment
NOTE: Conversation title was EDITED: Read the first three posts - #1..#3 - under the subtitle bind() not working because it was triggered by trying to make
require()
and.connect()
and all kinds of initializers to work correctly after save() and power recycle in a standalone operation using a sequencer as presented in post #6.In some initialization sequencing I had the construct that simplified to the essentials looks like this:
Above code uploaded to Espruino (PICO) and
onInit()
invoked in the console produces the following output:First invocation obviously works - because it is a standard invocation - but second - in
set Timeout(...
in line10
- fails.Afaiu
fnctn.bind(ctx)
binds thectx
object to the functionfnctn
so thatthis
in function is resolved toctx
context object. As examples shows, Espruino js does not.Changing line
10
todoes not help.
Changing
onInit() {...
toshows that
.bind()
is not working.I could reason that it would not work with multiple objects created with a constructor and
meth
had been defined on the prototype, and then bound to one of the created objects, and if multiple times bound, the last would win (...but it is not or neither,... see next post...).