Sunday, 16 February 2020

Extending Async Iterables

A few months ago I did a POC about extending Iterables with chainable common functional style methods like map and filter. Doing the same for Async Iterables is really simple, it's mainly a matter of replacing the internal for-of loop in each method with a for-await-of loop, and replacing [Symbol.iterator] with [Symbol.asyncIterator]!
I've put the code in a gist, I'll paste here just one section:

class AsyncExtendedIterable{
    constructor(iterable){
        this.iterable = iterable;
    }

    
    async *[Symbol.asyncIterator](){
        for await (let it of this.iterable){
            yield it;
        }
    }


    map(fn){
        //I need to use the old "self" trick, cause we can not declare a generator with an arrow function
        //so as we are using a normal function, "this" is dynamic, so we have to trap it via "self"
        let self = this;
        async function* _map(fn){
            //printDebug("_map");
            for await (let it of self.iterable){
                //printDebug("inside _map");
                
    //we can await in a non Promise value, so if we do "yield await" rather than just "yield", we are contemplating both a sync and an async fn mapping funtion
    //yield fn(it);
    yield await fn(it);
            }
        };
        let newIterable = _map(fn);
        return new AsyncExtendedIterable(newIterable);
    }

What is really sweet is that thanks to the fact that for-await-of loops work both with normal Iterables and AsyncIterables, we can use this AsyncExtendedIterable class with Synchronous Iterables. Furthermore, once our internal _map, _filter... functions have been declared as async, we can make them work fine with an async iteratee function (the function that we pass to the map, filter methods), by just adding an await to the call. This will work fine with sync and async iteratees because we can await synchronous calls without problem (the runtime just wraps the returned synchronous result in a resolved Promise). Just check the example in the gist.

No comments:

Post a Comment