Saturday 26 September 2020

JavaScript Arrays Oddities

As almost everything in the language, Arrays in JavaScript are not exactly the same as in other languages. In principle one thinks of an array as a continuous section of memory, where each item has the same size and hence it can be easily located based on its numeric index (you know: startAddress + (index * itemSize))... Well, this is not always the case in JavaScript.

First, in JavaScript arrays can be sparse (rather than dense). This means that if you create an array of a given size (e.g.: new Array(200);) but you only assign a value to some of those 200 positions, the unassigned ones are "holes" and do not take up space. Accessing one of those holes will retun an undefined value, traversing the array with for-of will also return undefined values, but traversing it with .forEach will skip the "holes". If we really had "holes" in the array (rather than having those positions pointing to "undefined") the Array can not be just a continuous piece of memory... Well, this article explains how it's implemented in the v8 engine. You can read there that for small arrays, a real array (continuous memory space) is used, with those empty spaces containing a "hole" value, so there's not any saving in memory space. If the array is big, then "a low level array" is no longer used, but a dictionary where the numeric indexes are the keys.

The article stops there, but there are more oddities with arrays. If you are familiar with Python (or with the last C# addition, indexes) maybe you have tried to use a negative index with your array. That sort of works in JavaScript, but not as you would expect. array[-1] does not refer to the last item in the array, but to a "-1" property (indeed as if you use any other string). So doing ar[-1] = "ax"; will add a property to the object:

 

> let ar = ["a","b"];
> ar;
[ 'a', 'b' ]
> ar[-1];
undefined
> ar[-1] = "Bonjour";
'Bonjour'
> ar[-1];
'Bonjour'
> ar;
[ 'a', 'b', '-1': 'Bonjour' ]
> 

So in a case like that I don't know how it is implemented. Maybe the "normal" part of the Array is maintained as a low level array and a dictionary is used only for the non numeric indexes, or maybe a dictionary is used for both numeric and non numeric indexes.

One additional comment. Both numeric and non numeric indexes are considered as "own properties" of the array. However, holes in the sparse array are not "own properties".

 

> ar;
[ 'a', 'b', '-1': 'Bonjour' ]
> ar.hasOwnProperty(1);
true
> ar.hasOwnProperty(-1);
true
> ar.hasOwnProperty(-2);
false

> let sparseAr = new Array(5);
> sparseAr[2] = "aa";
> sparseAr;
[ <2 empty items>, 'aa', <2 empty items> ]
> sparseAr.hasOwnProperty(0);
false
> sparseAr.hasOwnProperty(2);
true


Saturday 19 September 2020

Canvas Double Buffering

In the past I did a bit of Html Canvas programming (for example: this), and I never needed to resort to double buffering to avoid flickering. I did not pay much attention to it at the time, but the other day I came across some discussion as to whether double buffering was necessary. Though you can find some discrepancies and some samples claiming to cause flickering, the main answer is that No, you don't need to implement double buffering, which leads us to something more interesting, why not?

The fiddle in this answer is rather instructive, but what the author says Lucky for us every canvas implementation implements it (double buffering) behind-the-scenes for you. could be misleading.

If we have an understanding of how the event loop works in the Browser (or in node), we know that user interactions, timeouts, requestAnimationFrame... enqueue tasks in a macrotask queue (for the current discussion we can omit the microtask queue), and that the event loop will take tasks from that queue one by one, executing the corresponding javascript code, and updating the DOM (rendering) after each task is completed. This "updating the DOM after the ask is completed" is essential for this explanation. From the article:

Rendering never happens while the engine executes a task. It doesn’t matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.

So if we are drawing animations to a canvas (normally based on requestAnimationFrame), each animation frame javascript code will run as part of a Task, and when that frame code is completed, the rendering will happen. So all the sequential drawing calls that we do to the Canvas Context (clear the canvas, draw a rectangle, draw a circle...) won't be visible until the whole sequence of drawing operations is complete (the task is complete) and then the rendering-drawing takes place.

I don't think the Canvas element uses a second (hidden) canvas to draw on it during the Task (javascript) execution phase, and then draws that Canvas on screen during the rendering phase, I just think that all those drawing operations are stored, and then, during the rendering phase, they are painted to the canvas.

Tuesday 8 September 2020

Promise Inspection Part 2

In my previous post I said that probably there was an easier way than inheritance to implement synchronous inspection in Promises, so that's what this post is about.

This mechanism is used differently from the one shown last week. Rather than receiving an executor function and creating a Promise from it, we receive an existing Promise, and chain to her a new Promise that is expanded with the expected inspection methods (isFulfilled(), isRejected(), getValue(), getReason()). The code is pretty straight forward. The new Promise waits for the original Promise and when this one is completed or rejected it sets her inpection properties accordingly.

An interesting point is that indeed the new Promise does not hold the isFullfilled, value, reason... values (used by the corresponding isFullfilled(), getValue()... expansion methods) as data fields (that hence could be set from outside), but as variables that get trapped by the closures used for each of those expansion methods. This is an old trick used for creating private fields in JavaScript.

Donc, voilĂ  le code:

 

//returns a new Promise expanded with inspection methods
function enableSyncInspect(pr){
    //we trap these variables in the closure making them sort of private, and allow public access only through the inspection methods that we add to the new promise
    let isFulfilled = false;
    let value = null; //resolution result
    
    let isRejected = false;
    let reason = null; //rejection reason
    
    let isPending = true;

    //create a new promise that gets resolved-rejected by the original promise and gets expanded with inspection methods
    let prWrapper = pr.then(_value => {
        isPending = false;
        isFulfilled = true;
        value = _value;
        return _value;
    }, _reason => {
        isPending = false;
        isRejected = true;
        reason = _reason;
        return _reason;
    });

    prWrapper.isFulfilled = () => {
        return isFulfilled;
    }

    prWrapper.getValue = () => {
        return isFulfilled 
            ? value
            : (() => {throw new Error("Unfulfilled Promise");})(); //emulate "throw expressions"
    }

    prWrapper.isRejected = () => {
        return isRejected;
    }

    prWrapper.getReason = () => {
        return isRejected
            ? reason
            : (() => {throw new Error("Unrejected Promise");})(); //emulate "throw expressions"
    }

    prWrapper.isPending = () => {
        return isPending;
    }

    return prWrapper;
}


That we can use like this:

 

function formatAsync(msg){
    return new Promise((resFn, rejFn) => {
        console.log("starting format");
        setTimeout(() => {
            console.log("finishing format");
            resFn(`[[${msg}]]`);
        }, 2000);
    });
}

function printValueIfFulfilled(pr){
    if (pr.isFulfilled()){
        console.log("Promise resolved to: " + pr.getValue());
    }
    else{
        console.log("Promise NOT resolved yet");
    }
}

//async main
(async () => {
    let pr1 = formatAsync("Bonjour");
    let syncInspectPr = enableSyncInspect(pr1);

    console.log("isPending: " + syncInspectPr.isPending());

    //this fn runs in 1 seconds (while the async fn takes 3 seconds) so it won't be fulfilled at that point)
    setTimeout(() => printValueIfFulfilled(syncInspectPr), 1000);

    let result = await syncInspectPr;
    console.log("result value: " + result);
    
    printValueIfFulfilled(syncInspectPr);

})();

//Output:
// starting format
// isPending: true
// Promise NOT resolved yet
// finishing format
// result value: [[Bonjour]]
// Promise resolved to: [[Bonjour]]


As usual I've uploaded it into a gist.

Tuesday 1 September 2020

Promise Inheritance and Synchronous Inspection

The other day, taking a look into the advanced Promises provided by bluebirdjs I though about how to implement a very small and basic part of the funcionality, the Synchronous Inspection, that basically stores the value resolved by a Promise and allows accessing to it synchronously (read the doc for a real explanation). There are other ways to implement it, but I decided to use inheritance, and has been interesting enough to post it here.

In a previous post I already made use of Promise inheritance, but I did not need to take into account the resolve and reject callbacks passed to the executor function received by the constructor. In this case I wanted to override those resolve-reject callbacks, so that I would add my additional logic (setting the isFulfilled and value or isRejected and reason values) and then invoke the originals (the parent ones let's say) so that they perform their magic logic of invoking the continuations (the "then"-"catch" handlers). These callbacks are not exposed as methods in the Promise class (one could think of having implemented them as protected methods in another language...), so in order to get hold of them to use them, we create an internal Promise with an executor function that just will give us a reference to those original callbacks, and then we call the original executor function passing to it resolve-reject callbacks that perform our extra logic and then invoke the originals. Our "then" overriden method invokes the internal Promise parent method, so that all the logic performed by the Promise class to set the continuations is run.

The explanation above is really confusing, so you better just check the code of my SyncInspectPromise class:

 


class SyncInspectPromise extends Promise{
    constructor(executorFn){
        //compiler forces me to do a super call
        super(() => {});

        this._isFulfilled = false;
        this._value = null; //resolution result
        
        this._isRejected = false;
        this._reason = null; //rejection reason
        
        this._isPending = true;
        
        //we need to be able to invoke the original resFn, rejFn functions after performing our additional logic
        let origResFn, origRejFn;
        this.internalPr = new Promise((resFn, rejFn) => {
            origResFn = resFn;
            origRejFn = rejFn;
        });

        let overriddenResFn = (value) => {
            this._isPending = false;
            this._isFulfilled = true;
            this._value = value;
            origResFn(value);
        };

        let overriddenRejFn = (reason) => {
            this._isPending = false;
            this._isRejected = true;
            this._reason = reason;
            origRejFn(reason);
        };

        executorFn(overriddenResFn, overriddenRejFn);
    }

    isFulfilled(){
        return this._isFulfilled;
    }

    getValue(){
        return this.isFulfilled() 
            ? this._value
            : (() => {throw new Error("Unfulfilled Promise");})(); //emulate "throw expressions"
    }


    isRejected(){
        return this._isRejected;
    }

    getReason(){
        return this.isRejected()
            ? this._reason
            : (() => {throw new Error("Unrejected Promise");})(); //emulate "throw expressions"
    }

    isPending(){
        return this._isPending;
    }

    then(fn){
        //we set the continuation to the internal Promise, so that invoking the original res function
        //will invoke the continuation
        return this.internalPr.then(fn);
    }

    catch(fn){
        //we set the continuation to the internal Promise, so that invoking the original rej function
        //will invoke the continuation
        return this.internalPr.catch(fn);
    }

    finally(fn){
        return this.internalPr.finally(fn);
    }
}


And we can use it like this:

 

function sleep(ms){
    let resolveFn;
    let pr = new Promise(res => resolveFn = res);
    setTimeout(() => resolveFn(), ms);
    return pr;
}

function printValueIfFulfilled(pr){
    if (pr.isFulfilled()){
        console.log("Promise resolved to: " + pr.getValue());
    }
    else{
        console.log("Promise NOT resolved yet");
    }
}

(async () => {
    let pr1 = new SyncInspectPromise(res => {
        console.log("starting query");
        setTimeout(() => {
            console.log("finishing query");
            res("hi");
        }, 3000);
    });
    console.log("isPending: " + pr1.isPending());

    //this fn runs in 1 seconds (while the async fn takes 3 seconds) so it won't be fulfilled at that point)
    setTimeout(() => printValueIfFulfilled(pr1), 1000);

    let result = await pr1;
    console.log("result value: " + result);
    
    printValueIfFulfilled(pr1);

})();

//output:
// starting query
// isPending: true
// Promise NOT resolved yet
// finishing query
// result value: hi
// Promise resolved to: hi

As usual I've uploaded it into a gist.