Thursday 25 June 2020

Cancellable Async Function

Standard JavaScript Promises do not provide a mechanism for cancellation. It seems like there have been long discussions about it, but for the moment, nothing has been done. On the other side Bluebird.js provides a very powerful cancellation mechanism.

One major point is to clarify what cancellation means for us. Does it mean that it never resolves or that it gets rejected? Bluebird authors went through this thought process, and while in version 2.0 cancelling was rejecting, in 3.0 it means that it never resolves:

The new cancellation has "don't care" semantics while the old cancellation had abort semantics. Cancelling a promise simply means that its handler callbacks will not be called.

With Bluebird you can cancel a Promise chain, which can be very useful and I won't try to implement myself... I was thinking of a simpler case, when we have an async function performing multiple async calls via await and we would like to ask that method to stop "as soon as possible", which means waiting for the current async call to complete and not performing the remaining ones. .Net makes use of the Cancellation Token concept, and I'll use something similar for this case that I've just described. I'll allow both rejecting and just "abandoning". It mainly comes down to this function:

 

function checkCancelation(cancelationToken){
    if (cancelationToken && cancelationToken.reject){
        console.log("throwing");
        throw new Error("Rejection forced");
    }
    if (cancelationToken && cancelationToken.cancel){
        console.log("cancelling");
        //return a Promise that never resolves
        return new Promise(()=>{});
    }

    return false;
}

//to be used like this:
//let result = await (checkCancelation(cancelationToken) 
//        || getResultAsync());


that we'll use like this:

 

function getLastMessageId(){
 return new Promise(res => {
  setTimeout(() => res(111), 1500);
 });
}

function getMessageText(id){
 return new Promise(res => {
  setTimeout(() => res("this is the last message"), 1500);
 });
}


function formatText(txt){
 let formattedTxt = `[[${txt}]]`; 
 return new Promise(res => {
  setTimeout(() => res(formattedTxt), 1500);
 });
}

async function getLastMessageFormatted(cancelationToken){
    let id = await (checkCancelation(cancelationToken) 
        || getLastMessageId());
    console.log("ID obtained");
    
    let txt = await (checkCancelation(cancelationToken)
        || getMessageText(id));
    console.log("Message obtained");

    let msg = await (checkCancelation(cancelationToken)
        || formatText(txt));
    console.log("Message formatted");

    return msg;
} 

(async () => {
    console.log("-- test 1");
    let msg = await getLastMessageFormatted();
    console.log("message: " + msg);

    
    console.log("-- test 2");
    let cancellationToken = {};
    //reject after 1 second
    setTimeout(() => cancellationToken.reject = true, 1000);
    try{
        msg = await getLastMessageFormatted(cancellationToken);
        console.log("message: " + msg);
    }
    catch (ex){
        console.error("Exception: " + ex.message);
    }

    console.log("-- test 3");
    cancellationToken = {};
    //cancel after 1 second
    setTimeout(() => cancellationToken.cancel = true, 1000);

    //when cancelling we return a simple Promise, that won't keep the program running 
    //(it's real IO-timeout calls, not the Promise itself, what keeps the node.js loop running)
    //If I just want to keep it running longer, just use this keep alive timeout
    setTimeout(() => console.log("keep alive finished"), 10000);
    try{
        msg = await getLastMessageFormatted(cancellationToken);
        console.log("message: " + msg);
    }
    catch (ex){
        console.error("Exception: " + ex.message);
    }
})();

 

I've uploaded the above code to a gist.

Another simple case that I've implemented is having a Promise and preventing its "then handlers" from executing. Notice that I'm just cancelling from the initial promise (not from the one returned by then), while Bluebird's promise chain cancellation is invoked on the last promise returned (and goes all the way up the chain to cancel the active one, which I guess means that in Bluebird Promises are double linked). As you can see I use 2 different strategies for creating a new "cancellable Promise" from the original one.

 

function formatTextAsync(txt){
 let formattedTxt = `[[${txt}]]`; 
 return new Promise(res => {
  setTimeout(() => res(formattedTxt), 1500);
 });
}

function createCancellablePromiseStrategy1(pr){
 let cancelled = false;

 let cancellablePromise = new Promise(res => {
  pr.then(value => {
   if (!cancelled){
    res(value);
   }
   //else we never resolve
   else{
    console.log("promise has been cancelled");
   }
  })
 });
 
 cancellablePromise.cancel = () => cancelled = true;
 return cancellablePromise;
}

function createCancellablePromiseStrategy2(pr){
 let cancelled = false;

 //if the function ran by "then" returns a promise, the promise initially returned by "then" is resolved
 // when that other promise is resolved
 let cancellablePromise = pr.then((value) => {
  return new Promise(res => {
   if (!cancelled){
    res(value);
   }
   //else we never resolve
   else{
    console.log("promise has been cancelled");
   }
  });
 });
 
 cancellablePromise.cancel = () => cancelled = true;
 return cancellablePromise;
}


//let creationStrategy = createCancellablePromiseStrategy1;
let creationStrategy = createCancellablePromiseStrategy2;

let pr1;

const operation = async () =>{
 pr1 = creationStrategy(formatTextAsync("hi"));
 let result = await pr1; //if cancelling pr1 will never resolve, so the next line won't run
 console.log("result: " + result);
};

operation();
pr1.cancel();

 

Sunday 21 June 2020

Arsen (aka OEGP's lost recording)

If you've read this post you know how much I love that amazing Canadian (Quebecoise) ultra-dark screamo band, One Eyed God Prophecy. Many bands have been inspired by them (and by Uranus, that have managed to achieve much more of a cult status), but honestly I'd never found any band that sounded 100% like them, that had a song that I could place in the middle of the OEGP Lp and think that had been produced by them.

I've finally found that band! Arsen (aka Konig the Monster). This German band released stuff in the 2002-2003 period, right when I was starting to move from Screamo to other sorts of sounds, so they went unnoticed for me. I've recently found about them and they are absolutely amazing. Almost all their songs are a brutal display of darkness, rage, violence and sadness, there's one Erde Meldet Sich Zuruck that particularly stands out and obsesses me. It's as if these guys had found a lost OEGP recording and re-recorded it!

Reading the interview linked above you'll see that there's a bunch of bands associated to the smart guys (and lady) that made up this act (and in turn, some of those bands are associated via other members to highly praised bands like Tristan Tzara, Louise Cyphre...) I already knew one of these excellent bands, Saligia that even if I did not get fully hooked into them, really surprised me with their very Uranus like sound. The other band, Republic of Dreams has been a beautiful discovery. They've been around for many years, playing dark, violent, German ("German" here involves that characteristic metallic touch) Scremo. Their last split with another amazing new band , Alles Brennt is particularly worthy of a deep listen.
Enjoy the darkness!!!

Saturday 13 June 2020

Lazy Objects via JavaScript Proxy

Related to my last post about Lazy promises it came to my mind the idea of creating a Generic Lazy object. A long while ago I had already tinkered with Lazy objects in JavaScript. At the end of that post I mention that sometime in the future I should try a different and much more transparent approach, using proxies, as I had done some weeks earlier in C#. Well, it has taken more than 7 years to turn that "in the future" into present... but finally, here it is.

The idea is pretty simple, we create a Proxy object with a get and set traps. The first time get or set are invoked, the lazy object has to be created. For that, the get and set trap functions keep as closure state the constructor function and the parameters to use in order to create the lazy object. When we create a Proxy we provide as first parameter the object that we are proxying. In this case such object does not exist, the proxy is on its own just to allow us to later create the object, so I was intending to pass null as parameter, but the compiler won't allow that, so I pass and "empty object": {}.
I'm thinking now that I could have used that object to hold the constructor and arguments, rather than keep them as closure variables... well, both approaches are equally valid.

I paste here the code, and you can also find it in this gist

 


//Create a Proxy for the lazy construction of an object
function buildLazyObject(constructorFn, ...args){
    let internalObject = null;
    //we don't care about the target, but compiler does not allow a null one, so let's pass an "empty object" {}
    return new Proxy({}, {
        get: function(target, property, receiver){
              internalObject = internalObject || (() => {
                console.log("Creating object");
                return new constructorFn(...args);
            })();
            
            //this way, if it's a method, internal calls to other methods of the class would go through the proxy also
            //for this case it makes no sense
            //return internalObject[property];
            
            let item = internalObject[property];
            if (typeof(item) === "function"){
                return function(...args){
                    return item.call(internalObject, ...args);
                };
            }
            else{
                return item;
            }
        },

        set: function(target, property, value, receiver){
            internalObject = internalObject || (() => {
                console.log("Creating object");
                return new constructorFn(...args);
            })();
            
            internalObject[property] = value;
        }
            
    });
}

//--------------------------------------------

class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }       

    sayHello(){
        return "Bonjour, Je suis " + this.name + " et j'ai " + this.age + ", ma ville est " + (this.city || "inconnu");
    }
}

let p1 = buildLazyObject(Person, "Francois", 2);
console.log("Proxy created");

console.log(p1.sayHello());

console.log("Second call");

console.log(p1.sayHello());

let p2 = buildLazyObject(Person, "Didier", 4);
console.log("Proxy created");
p2.city = "Marseille";
console.log("after set value");

console.log(p2.sayHello());
 
// Proxy created
// Creating object
// Bonjour, Je suis Francois et j'ai 2, ma ville est inconnu
// Second call
// Bonjour, Je suis Francois et j'ai 2, ma ville est inconnu
// Proxy created
// Creating object
// after set value
// Bonjour, Je suis Didier et j'ai 4, ma ville est Marseille



Friday 5 June 2020

Lazy Promises

Last year I mentioned in this post that one of the alleged advantages of using Observables over Promises for single values is that Observables are lazy (the "executor" function of an Observable won't be run until the Observable gets subscribed). Conversely, the "executor" function of a Promise gets executed as soon as the Promise constructor is invoked. As I said in that post, I don't think that we need the laziness much often, and for me the cleanliness of a code written with async-await absolutely beats Observables and RX (remember I'm taking about single values). But anyway what if we need laziness? Well, implementing a Lazy Promise is pretty simple.

We need to store the "executor" function when the Lazy Promise is created, and use it to create the real Promise when we first need it, that is, the first time "then" is invoked.

<
class LazyPromise extends Promise{

    constructor(creationFn){
        //compiler forces me to do a super call
        super(() => {});
        console.log("creating LazyPromise");
        this.initialized = false;
        this.creationFn = creationFn;
    }

    then(resFn, rejFn){
        if (!this.initialized){
            console.log("creating Real Promise");
            this.initialized = true;
            this.internalPromise = new Promise(this.creationFn);
        }

        return this.internalPromise.then(resFn, rejFn);

    }

}

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


(async () => {
    let pr1 = new LazyPromise(res => {
        console.log("starting query");
        setTimeout(() => {
            console.log("finishing query");
            res("hi");
        }, 2000);
    });
    
    console.log("before sleep");
    await sleep(5000);
    console.log("after sleep");
    let result = await pr1;
    console.log("promise returns: " + result);
    
})();

As you can see, I'm inheriting from Promise. It's not that we are reusing anything in the Parent class, but well, a LazyPromise feels to me like a specialized Promise, so regardless of being in a type-checking free language, inheritance seems appropiate. Additionally I was thinking that in order for result = await myLazyPromiseInstance to work we would need it to inherit from Promise. I was assuming that the runtime would use instanceof to see that we were awaiting a Promise and if not wrap the value in a Promise (cause we know that we can await non-promise values). Well, indeed await does not check if it's a Promise, it checks if it's a thenable (it has a then method) in a nice example of duck typing, which makes pretty much sense. This thenable thing is mentioned in the MDN doc for Promise.resolve. This means that I can rewrite the above code using composition:

//we don't need to inherit from Promise, await works fine with just a "thenable"
//notice in the second part of the code that Promise.resolve of a thenable returns a new Promise rather than the thenable
class LazyPromise {

    constructor(creationFn){
        console.log("creating LazyPromise");
        this.initialized = false;
        this.creationFn = creationFn;
    }

    then(resFn, rejFn){
        if (!this.initialized){
            console.log("creating Real Promise");
            this.initialized = true;
            this.internalPromise = new Promise(this.creationFn);
        }

        return this.internalPromise.then(resFn, rejFn);

    }

}

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

(async () => {
    let pr1 = new LazyPromise(res => {
        console.log("starting query");
        setTimeout(() => {
            console.log("finishing query");
            res("hi");
        }, 2000);
    });

    console.log("before sleep");
    await sleep(5000);
    console.log("after sleep");
    let result = await pr1;
    console.log("promise returns: " + result);
 })();

An additional point. As we know when we pass over a Promise to Promise.resolve, it returns that provided Promise rather than a new Promise. If we pass it over a thenable, it will return a new Promise, not just the thenable, but that new Promise "follows" the thenable (meaning that it gets resolved when the thenable is resolved.)

    let prA = new Promise(() => {});
    let prB = Promise.resolve(prA);
    console.log(prA === prB); //true

    //LazyPromise is a thenable
    prA = new LazyPromise(() => {});
    prB = Promise.resolve(prA);
    console.log(prA === prB); //false

    let prC = new LazyPromise(res => {
        console.log("starting query");
        setTimeout(() => {
            console.log("finishing query");
            res("Bye");
        }, 2000);
    });

    let prD = Promise.resolve(prC);
    result = await prD;
    console.log(result); //Bye
    //as explained in MDN, it wraps the thenable in a new Promise, but this new promise is not resolved until the thenable is resolved
    //the returned promise will "follow" that thenable, adopting its eventual state;