Problem with asynchronous code in class ES6

Posted on
  • Hi !

    class A {
        constructor(x, y){
        //class field...
        }
    
        AsyncFuncA(){
        //code...
        }
    
        run(){
        //call async code
            setInterval(() => {
                this.AsyncFuncA();
            }, 1000)
        }
    }
    
    class B {
        constructor(x, y){
            super(x,y);
        }
    
        AsyncFuncB(){
           //code...
        }
    
        run(){
        //call async code
            super.run();
            setInterval(() => {
            // Todo...
                this.AsyncFuncB();
            }, 2000);
        }
    }
    /***************************************­*******/
    let ob = new B(x, y);
    ob.run();
    

    an error occurs when you call run() -> this.AsyncFuncA()
    Web IDE:

    "Uncaught Error: Function "AsyncFuncA" not found!
    at line 248 col 6
    this.AsyncFuncA();

      ^
    

    in function called from system"

    how can this be fought ?

  • Hi,

    Reading your question more carefully, your problem is the value of this in the setInterval, probably the global context which is not what you want.
    This can be resolved by sending the right context object for this as an added last argument in setInterval or seTimeout as below.

    ...
    var timeoutID = setTimeout(function (self) {
      console.log(self); 
    }, 500, this);
    ...
    

    So, your object, instance of class, will be called self in the setInterval/SetTimeout at execution time...

    You should look at the reference of setInterval and this non Espruino thread or this other one

  • I disagree with you

    1. This code works without modification under NodeJS;
    2. This code works if it is issued in the prototype style of ES5.

    It seems to me that the problem is in Espruino

  • Fri 2019.03.01

    What version of Espruino is running/flashed?

    http://www.espruino.com/Features

    Not all ES6 supported as of this post

  • However, Espruino is designed to run on very low level mcu's and it doesn't claim any real compatibility with nodejs or other standards.
    You should search this forum about that and read the reference rather than other sites.

    Maybe the Tessel board would suit you better?

  • EDIT: ...to cut to the chase:

    Conclusion: Everything is just fine... @Gordon: 100 pts, @candidate: 0 - GAME OVER!

    ...TL;DR:... the rest of this and following posts....


    hold your horses, @asez73...

    @Konkery, I cannot speak for all details of JS in Espruino, but I get out of this challenge with the basic understanding and applying of this - the context object - which worked from the very beginning and is still working. JS has its quite own understanding of the context - the this - object... - and because JS is really really late binding, this is evaluated when executed. Furthermore, the global functions have in the browser the global (variable) window as the object... therefore, when a function is passed to a (window.)setInterval(f(){ console.log(this); },1000); and the function refers to this, this is the global window object... and in Espruino - where we do not have the global window - it refers to the global global, the equivalent of the browser's global window...

    With that, @Konkery, you can really beat it in even more than one way...

    • one is to define a local variable var _this = this; before the setInterval() and use it - _this in its function... (sometimes I just use var _ = this; to be short).
    • another one is to pass this as variable by passing it as 2nd+ parameter when setting up the interval: setInterval(function(_){ _.xyz(); },1000,this);.

    Above uses just pure basic JS... yes, I know that => has its fan base... see ES6 In Depth: Arrow functions article from MSDN's Jason Orendorff - but it has its caveats as well... for being unable to type function spelled incorrectly though is - for me - not one of them...

    In fairness, I cannot say wether node.js does it right... or browser or Espruino does it right, because - as said earlier - the caller of the function is not the object that does setup the interval - setInterval(... - it is the underlaying system... and I would not know where this would be specified... so I'm not surprised that that you and I get this error when coding it as in post #1.

    Finally, @Konkery, I beg you for a favor and edit the conversation (title) to something like ```How do deal with this this in JS when I get error message "xyz not found!"

    PS - Disclaimer: I could be wrong... would not be the first time... and also not the first time I have to blame myself... for a nice - stingy - laughter about myself!

  • An Arrow function is different from a normal one in that its context is not bound and is taken from the scope lexically above it. Here is an article explaining the differences.

    The code looks as correct ES6 wrt to context use and it should work (I had to fix class inheritance and undeclared variable use in order to run it in Chrome). It looks like an Espruino bug.

  • I studied Gordon's article and found no contradictions in his code.
    Article - "How ES6 classes work"
    http://forum.espruino.com/conversations/­327037/

  • @allObjects Does not work:

    one is to define a local variable var _this = this; before the setInterval() and use it - _this in its function... (sometimes I just use var _ = this; to be short).
    another one is to pass this as variable by passing it as 2nd+ parameter when setting up the interval: setInterval(function(_){ _.xyz(); },1000,this);.

  • Sat 2019.03.02

    Article - "How ES6 classes work"

    Yes, quite nicely done, and was so in response to issues I had with large classes within modules using require( ) back in October 2018. Those steps helped me pin-point one culprit similarly to:

    I noticed that from #1 above:

    at line 248 col 6

    as you have quite a few lines of code, are you using that chunk in a module and have Espruima on perhaps? My issues were resolved by turning minification off. (as ES6 reverts to ES5 under some conditions)

    See heading "Using modules and classes" from your #9 link above.


    Espruino change log

    https://github.com/espruino/Espruino/blo­b/master/ChangeLog

    Release dates

    https://github.com/espruino/Espruino/rel­eases

    Several enhancements were added between October and January related to ES6 and had me re-flash from 1v94-1v99-2v00-2v01

    1v96 has been around for a year and ES6 changes were peppered in current releases. Still in it's infancy.


    As @opichals #7 most likely is using a recent release and has indicated:

    It looks like an Espruino bug.

    It is quite possible that it is just that, or that all nuances of ES6 are just not implemented yet. @Gordon most likely will be the definitive knowledge source here.

  • @Konkery, did you even try? where is your 'correct' code and the console output? ...because it just works...

    Here the fixed code (w/ B extending A) with the two presented options for tackling the correct context:

    // someES6Test01.js
    //
    class A {
        constructor(x, y) { // class field...
          this.x = x;
          this.y = y;
        }
        asyncFuncA() { // code...
            console.log("A.asyncFuncA()");
        }
        run() { // call async code
            var _this = this;
            setInterval(() => {
                _this.asyncFuncA();
            }, 1000);
        }
    }
    class B extends A {
        constructor(x, y){
            super(x,y);
        }
        AsyncFuncB() { //code...
            console.log("B.asyncFuncB()");
        }
        run() { // call async code
            super.run();
            setInterval((_) => { // Todo...
                _.AsyncFuncB();
            }, 2000, this);
        }
    }
    /***************************************­*******/
    let x = 1; let y = 2;
    let ob = new B(x, y);
    
    function onInit() {
      ob.run();
    }
    
    setTimeout(onInit,500); // while dev'n```
    

    And related console output (PICO w/ Espruino 2v01):

    >
     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v01 (c) 2018 G.Williams
    >
    A.asyncFuncA()
    A.asyncFuncA()
    B.asyncFuncB()
    A.asyncFuncA()
    A.asyncFuncA()
    B.asyncFuncB()
    A.asyncFuncA()
    A.asyncFuncA()
    B.asyncFuncB()
    A.asyncFuncA()
    >reset() // entered to stop running intervals
    =undefined
    > 
    

    Since there were other bugs in the code - missing parent class reference extends B- , I'm suspicious about even needing the work around - with correct code... I try now the code with out the tackling of the this context, but with fixed class B... (and some more identifiable output in the console):

    AND IT WORKS TOO - I assume now that you ran a different code than what you pug in post #1,... and that code is buggy on some other terms...

    // someES6Test01b.js
    //
    class A {
        constructor(x, y) { // class field...
          this.x = x;
          this.y = y;
        }
        asyncFuncA() { // code...
            console.log("A.asyncFuncA()",getTime() % 100);
        }
        run() {
            //call async code
            setInterval(() => {
                this.asyncFuncA();
            }, 1000);
        }
    }
    class B extends A {
        constructor(x, y) {
            super(x,y);
        }
        asyncFuncB() { // code...
            console.log("B.asyncFuncB()",getTime() % 100);
        }
        run() { // call async code
            super.run();
            setInterval(() => { // Todo...
                this.asyncFuncB();
            }, 2000);
        }
    }
    /***************************************­*******/
    let x = 1; let y = 2;
    let ob = new B(x, y);
    
    function onInit() {
      ob.run();
    }
    
    setTimeout(onInit,500); // while dev'n
    

    ...and related output:

    >
     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v01 (c) 2018 G.Williams
    >
    A.asyncFuncA() 64.51624393463
    A.asyncFuncA() 65.51623725891
    B.asyncFuncB() 65.51758861541
    A.asyncFuncA() 66.51625156402
    A.asyncFuncA() 67.51624679565
    B.asyncFuncB() 67.51758384704
    A.asyncFuncA() 68.51623630523
    A.asyncFuncA() 69.51630210876
    B.asyncFuncB() 69.51768684387
    A.asyncFuncA() 70.51631069183
    A.asyncFuncA() 71.51631927490
    B.asyncFuncB() 71.51773548126
    A.asyncFuncA() 72.51630496978
    A.asyncFuncA() 73.51627349853
    B.asyncFuncB() 73.51770114898
    >reset() // entered to stop running intervals
    =undefined
    

    Conclusion: Everything is just fine... @Gordon: 100 pts, @candidate: 0 - GAME OVER

  • Sat 2019.03.02

    I wish I had as much time as you do @allObjects to try all these situations. ;-)
    I'm not worthy. (ref Wayne's World)

    Good catch Line #18
    class B extends A


    That and; It is most likely 1v96 that is causing the headache as I pointed out in #11 and that @opichals may not have tested using 2v01 as did you.
    (ref mods to extends-super and constructor scope that occurred 2v00-2v01)

    Law of unintended consequences not keeping up on current releases. . . .

  • @Robin, I find it troubling to make a statement and not providing the prove for it. Agreed, there is not always time for it... but then the attitude helps setting the mending tone... (I know also that times have changed since I went to kindergarten: now, alt-facts seem to become truth when repeated often enough...).

  • ' now, alt-facts seem to become truth when repeated often enough...'

    Ahhhh, a reference to our failed media. . . . Fake News!!



    While I agree that #10 could have had a bit more substance, It might be that the combination of steps led our candidate to draw an incorrect conclusion, thus not allowing further progress. On occasion, I've been stymied on what I've believed to be an absolute, only to realize the next day there was in fact, another option.

  • Btw, the media is only the carrier... the feeders sit somewhere else... It is always easier to shoot the messenger than taking responsibilities for the actions. - ...and as for existence of any issue, it needs at least two non-political (or political?) parties that cannot find a solution which actually benefits the non-involved 3rd one, even though it is all about exactly that one.

  • Console conclusion at implementation of your code:

        ____       __
       /  _/_____ / /__ _____ ____ _
       / / / ___// //_// ___// __ `/
     _/ / (__  )/ ,<  / /   / /_/ /
    /___//____//_/|_|/_/    \__,_/
    Based on Espruino 1v96.43
    (c) 2018 G.Williams, Amperka LLC
    Support the work of core developers:
    http://espruino.com/Donate
    >
    =undefined
    Uncaught Error: Function "asyncFuncA" not found!
     at line 1 col 7
    _this.asyncFuncA();
          ^
    in function called from system
    
  • It is similar that in version 1v96.43 it does not work...

  • Sun 2019.03.03

    @Konkery please post the results of typing process.env

    >process.env
    ={
      VERSION: "2v00.103",
      GIT_COMMIT: "df8a910",
      BOARD: "PICO_R1_3",
      FLASH: 393216, RAM: 98304,
      SERIAL: "25005800-13513335-32373134",
      CONSOLE: "USB",
      MODULES: "Flash,Storage,hea" ... "v,crypto,neopixel",
      EXPTR: 536871088 }
    > 
    
    



    I don't have ver 1v96 flashed

    and, was the following done? Turn off minification

  • Hi,
    The reason is clearly established, it is in firmware 1v96.43,
    it doesn't work in her.
    To change the firmware at the moment I can not, the Board is not original (Amperka). Checked it on the original Board version 2v01
    there the code is executed correctly.
    I will wait for the firmware update.
    Thanks everyone !

  • ={
      "VERSION": "1v96.43",
      "GIT_COMMIT": "c975a9a",
      "BOARD": "ISKRAJS",
      "FLASH": 1048576, "RAM": 196608,
      "SERIAL": "3b004900-07513535-31393131",
      "CONSOLE": "USB",
      "MODULES": "Flash,Storage,fs," ... "t,crypto,neopixel",
      "EXPORTS": { "jsvLockAgainSafe": 105081, "jsvUnLock": 105055, "jsvSkipName": 111873, "jsvMathsOp": 91429,
        "jsvNewWithFlags": 105193, "jsvNewFromFloat": 105361, "jsvNewFromInteger": 105397, "jsvNewFromString": 110413, "jsvNewFromBool": 105381,
        "jsvGetFloat": 112097, "jsvGetInteger": 111285, "jsvGetBool": 112657, "jspReplaceWith": 29177, "jspeFunctionCall": 80681,
        "jspGetNamedVariable": 80505, "jspGetNamedField": 85101, "jspGetVarNamedField": 84825 },
      "EXPTR": 536871104 }
    
  • @allObjects @Konkery Unfortunately I did not test it in Espruino before so sorry for pronouncing it a problem.

    I can confirm now that for the RELEASE_1V96 this doesn't work as shown in the comments above and works appropriately in master.

  • EDIT: Forum update occurred while in edit mode #18 so this entry appears mis-placed #20 #21 #22



    Sun 2019.03.03

    In reply to #18

    I copied the source someES6Test01.js from #12 to the clipboard and uploaded to a Pico running 2v00

    Turn off minification

    >
     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v00.103 (c) 2018 G.Williams
    >
    A.asyncFuncA() 59.53073978424
    A.asyncFuncA() 60.53069114685
    B.asyncFuncB() 60.53212070465
    A.asyncFuncA() 61.53070640563
    A.asyncFuncA() 62.53075027465
    B.asyncFuncB() 62.53218650817
    A.asyncFuncA() 63.53072834014
    A.asyncFuncA() 64.53073310852
    B.asyncFuncB() 64.53215503692
    A.asyncFuncA() 65.53074169158
    A.asyncFuncA() 66.53076267242
    
    



    I repeated the test with the second example and got the same results.

    2v00 and 2v01    a-okay

    Conclusion: Everything is just fine... @allObjects BONUS points for both working examples!!



    Attempting to run on 1v94 results in a constructor error (as expected) as this wasn't implemented as of that release.
    From change log: 1v96 Add ES6 classes and 'super'



    I'd reflash to the current version.



    Ahhhh, I see why the reluctance to do so:

    https://github.com/amperka/espruino-modc­at

    and/then I'd contact:

    Board: http://amperka.ru/product/iskra-js

    as Amperka boards are not supported

    http://www.espruino.com/Other+Boards
    https://github.com/espruino/Espruino/iss­ues/930

    Consider contributing to Espruino with an authentic board purchase for active support, especially for the amount of time utilized in chasing this issue down. Would have had this working right out of the gate. . . .



    EDIT: Forum update occurred while in edit mode #18 so this entry appears mis-placed #20 #21 #22

  • @Konkery, thank your for clarifying the input about version as well as board and build. I apologize for jumping to conclusions with assumptions I did not verify with you before. The main assumption were the version and that you had updated to it after failing with outdated ones. Version 2+ brought really substantial enhancement and extensions. Even though ES6 is mainly syntax sugar, it make code really much more readable when going down the class based oo programming path.

    You can get there too by some poly filling or by using some frameworks - such as dojo @ dojotoolkit.org did quite consistently and successfully more than a decade ago (from which, btw, dynamic modularization w/ requireJS comes from) - but it is always stays a bit messy. You can RYO by implementing only simple inheritance things using mixins - extensively used by dojo and described, for example, in this Medium.com article from Eric Elliot about Composing Software, hinting relation to software as art.

    On the other hand, inheritance is quite overrated... because the things in the world do inherit, but not in the narrow sense of class based modeling, but more in the prototype based way. Interestingly this term is use in modeling in scale... model trains, etc. To me this prototype based inheritance means: looks like, but is its very own thing with its very own knowledge and behavior. So in most cases you choose composition over inheritance, because this gives you way more freedom to do things.

    Another issue with inheritanc vs. mixin/composition in micro controller context is the resources: from a maintenance point of view of supporting a library, class based / prototype based inheritance comes in handy... but at runtime, you rarely have multiple versions of a sensor or alike at that would asks for software to support inheritance for a gain.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Problem with asynchronous code in class ES6

Posted by Avatar for Konkery @Konkery

Actions