• Two more cents... about save() and power-on reset / power cycle... actually only 1 cent for now, since this post covers only delays in detail. Callbacks are covered in a next post.

    @Spocki and @MarcoM - and @Ollie, here you go:

    Think about your electronics / hardware: some things you time - delay - with an RC-circuit, and some you setup for trigger - callback - on a rising or falling edge. That sums it up quite simply... and may be a bit too simply, but it is a good starting point.

    Complexity arises then when you want to bring a lot of these into sequence and and more so when it is a mix and match. Not much different is it with software. The means are a bit different, but you still need to do the proper setup and do the enabling for the right time.

    As you know from electronics, some ICs have power-on / reset sequencing /setup / stabilization dependencies, and you have to wait before you can 'use' the IC. Even Espruino with its STM MC and the elaborate Espruino firmware have a power on reset sequence. The sequence is a combination of various hardware and (low level / internal) software tasks, after which - at last - Espruino firmware invokes onInit() function. Therefore, you put the start of your code into this onInit() function (or in function(s) invoked within onInit(). onInit() was there from the beginning, but in recent releases E.on("init",aFunction) is available as alternative. All functions you want to have invoked on power-up / reset you define and register with, for example, something like this:

    var globalVarSetInFuncA = 0;
    var globalVarSetInFuncB = 0;
    function yourStartFuncA() { globalVarSetInFuncA = 10; }
    function yourStartFuncB() { globalVarSetInFuncB = globalVarSetInFuncA + 5; } 
    function yourStartFuncC() {  
      console.log(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join(""));
    } 
    E.on("init",yourStartFuncA);
    E.on("init",yourStartFuncB);
    E.on("init",yourStartFuncC);
    

    All function registered with E.on("init",...) are also invoked after Espruino is ready for you in the same sequence as you registered them. The output in the console is:

    A: 10, B: 15

    Doing the same with onInit() instead of E.on("init",...) looks like this:

    var globalVarSetInFuncA = 0;
    var globalVarSetInFuncB = 0;
    function yourStartFuncA() { globalVarSetInFuncA = 10; }
    function yourStartFuncB() { globalVarSetInFuncB = globalVarSetInFuncA + 5; } 
    function yourStartFuncC() {  
      console.log(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join(""));
    } 
    function onInit() {
        yourStartFuncA();
        yourStartFuncB();
        yourStartFuncC();
    }
    

    You can run the examples above one by one by pasting them into the editor pane of the Espruino Web IDE and then upload them to the board.

    All the above works nicely - in intended sequence - when these functions are simple, blocking, synchronous function. Blocking means:

    • While the function is running, nothing else of your code is running.
    • The function completes all its related work.
    • When function returns control, all results are available.

    Unfortunately - and actually fortunately - the world is not that simple: there are functions that need some time to complete and they are implemented the way that they do not block the processor so that the processor can execute other things 'at the same time'... for the common benefit.

    Some function may take specified time to execute, and some use some unspecified time - unspecified in the sense that they depend on something else to happen before they can complete their work. Such functions behave asynchronously, non-blocking, which means:

    • While the 'function related work is done', other parts of your code can run.
    • The function 'moves on' before all its related work is completed.
    • When function returns control, not all expected results are available.

    That puts the simple - linear, sequently - invocation into trouble!

    Assume that the work of yourStartFuncA() in above example code takes a second to complete. For example, a sensor needs to 'warm up' before it can perform the (first) measurement and store its value in globalVarSetInFuncA. therefore, you need to give it time to do so. We emulate this using

    function yourStartFuncA() { setTimeout("globalVarSetInFuncA = 10;", 1000); }
    

    This adds some quirk to our simple initialization work, namely:

    setTimeout("source code", timeoutTimeInMilliseconds):

    • Tells Espruino to 'delays' the execution of globalVarSetInFuncA = 10; by 1000 milliseconds.
    • Lets the function 'move on' before the execution of the assignment.
    • Returns control before the result is available in globalVarSetInFuncA.

    Which means - with given initialization code using E.on("init",...) or onInit() { ... } - that our console output will show:

    A: 0, B: 5

    Not exactly what we expect...

    See yourself by using onInit() example with above setTimeout modification in yourStartFuncA().

    Indeed you will see the unexpected result... But:

    We can easily fix that, because we know what really happens. We fix it by delaying 'everything' (in the initialization) after invocation of yourStartFuncA() for as much time it takes to complete the work of yourStartFuncA(). To be safe, we chose 1100 ms. For example (using onInit()):

    function onInit() {
        yourStartFuncA();
        setTimeout("yourStartFuncB(); yourStartFuncC();", 1100);
    }
    

    Run the the code with above modification in onInit() and you will get the expected console output.

    A: 10, B: 15

    Having more than just one 'delaying' function in the initialization sequence will challenge the statement because we know what's going on therefore we can easily take care of dalays.... That's when other options have to come in, and that is a sequencer that allows timing...

    For additional details see posts in conversations:

    To make a timed initializer - using gCtl controller object - in link above: just put the cb() callback invocation into the (or a) setTimeout() with the delay as needed, and add the initializer to / register the initializer with gCtl, just like you do for the other functions:

    // g(lobal)Log(ging) function:
    var gLog = function(v) { 
      console.log(v); // NOTE: COMMENT THIS LINE FOR DISCONNECTED RUN
    };
    // 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](functi­on(ok,txt){_t.ini(ok,txt);});},1);}  
        } else { /* initializer is[i]() failed */
          /* code to handle failures... may be retries/start over... */
      } }
    , run: function() { this.i = -1; this.ini(true); } // run() invoked in onInit()
    };
    
    var globalVarSetInFuncA = 0;
    var globalVarSetInFuncB = 0;
    
    function yourStartFuncA(cb) { 
      globalVarSetInFuncA = 10;
      cb(true,"yourStartFuncA");
    }
    function yourStartFuncB(cb) { 
      setTimeout(function(){ 
          globalVarSetInFuncB = globalVarSetInFuncA + 5;
          cb(true,"yourStartFuncB");
        }, 1000); 
    }
    
    function yourStartFuncC(cb) {  
      gLog(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join(""));
      cb(true,"yourStartFuncC");
    } 
    
    function yourContinuousOperationFunc(cb) {
      gLog("Now entering my continuous operation function");
      //...
    }
    
    gCtl.adi(yourStartFuncA);
    gCtl.adi(yourStartFuncB);
    gCtl.adi(yourStartFuncC);
    gCtl.adi(yourContinuousOperationFunc);
    
    function onInit(){ gCtl.run(); }
    

    NOTE 1: IN NO PLACE YOU SEE save() AS PART OF THE CODE. It is though entered and executed in the console AFTER UPLOADING THE CODE.

    Note 2:

    Holding on to the module with an extra variable as in the code in the post where the gCtl controller object was introduced, may inadvertently - depending on how the module is implemented - hold on to some extra memory. Instead of holding on to the module with an extra variable and later referencing to the mdoule using the variable in the typical .connect(), again a require() could be executed, which - afaie(expect) - would go after the module cache and pull it from there. @Gordon may shed some light on possible memory issues. (In the client-side / browser world where for this approach requirejs.org's require() is used, a once (over the network) required module is cached (in a module cache) and can be referenced quickly again:

    require(
       ["modules/path/name1","modules/path/name­2"]
      ,function(module1,module2){
      var connection1 = module1.connect(...);
      module2.connect(connection1); // assume to be a statefull singleton
      ...
    });
    

    In some other place, later on, module2 would deliver the very same module - but now connected - as above:

    require(
       ["modules/path/name2"]
      ,function(module2){
      module2.doXyz(...);
      ...
    });
    

    node.js (nodejs.org)'s require() philosophy - what Espruino leans on - is similar, because it was born server-side and therefore most importantly synchronously vs as require() in the browser, where everything is best pulled asynchronously in order to optimally use the network connections. The browserify technique is now available to use node.js oriented code also in the browser, in order to use the very same source code both client AND server side. Therefore - in order to share the same Javascript code on Espruino and on your servers, use node.js for your server side implementations rather than any .cgi or Java or PHP or Python or ... You can use Python if you have MicroPython instead of Espruino... MicroPython was born - mind, distance and time wise - not far away from Espruino ;-)

About

Avatar for allObjects @allObjects started