1
\$\begingroup\$

Temporary storing the element(s) returned by document.querySelector and document.querySelectorAll methods could improve efficiency by calling the methods once when it is known that the document has not been changed. Following it is a buffered source, it supports passing in a parameter of type function returning a value and reseting to call the source that it is buffering to retrieve the sourced value on demand. When it is initialised it calls the passed in source and stores the result, if the result it is undefined schedules another call to the source to be run when the element it is first time requested:

var buffered = { source: function(bufferee) {
    
    var enabler = { supply: (purpose) => {
        
            var vain = purpose ? () => purpose() : () => false;
            var enablers = [vain];
            
            return { shift: function() { return enablers[0](); }
                   , schedule: function(schedulee) { 
                       enablers.unshift( () => { 
                                             enablers.shift();
                                             return schedulee();
                       });
                       return this;
                   }
            };
    } };
    
    var returnee = bufferee();
    var singularity = enabler.supply(() => returnee);
    if ( !returnee ) {
        singularity.schedule(() => {
            returnee = bufferee(); return returnee;
        });
    }
    
    return { element: () => singularity.shift()
           , reset: () => { returnee = bufferee(); } };
} };

Following an example to show case the supported features:

<html>
<head>
<title>Buffered source...</title>
<style type="text/css">
.display {
    bottom: 0;
    height: 50%;
    position: absolute;
    width: 100%;
}
.viewport {
    bottom: 0;
    overflow: hidden;
    position: absolute;
}
.field { height: 50%; }
.dynamic {
    background-color: #eee;
    border-radius: 50%;
    height: 200px;
    width: 200px;
    margin: auto;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}
</style>
<script type="text/javascript">
var element = { of: (selector) => document.querySelector(selector) };

var buffered = { source: function(bufferee) {
    
    var enabler = { supply: (purpose) => {
        
            var vain = purpose ? () => purpose() : () => false;
            var enablers = [vain];
            
            return { shift: function() { return enablers[0](); }
                   , schedule: function(schedulee) { 
                       enablers.unshift(() => { 
                           enablers.shift(); return schedulee();
                       });
                       return this;
                   }
            };
    } };
    
    var returnee = bufferee();
    var singularity = enabler.supply(() => returnee);
    if ( !returnee ) {
        singularity.schedule(() => { returnee = bufferee();
        return returnee; });
    }
    
    return { element: () => singularity.shift()
           , reset: () => { returnee = bufferee(); } };
} };

var vainer = { supply: (purpose) => {
    
    var vain = purpose ? () => purpose() : () => false;
    var vainers = [vain];
    
    return { shift: function() { return vainers[0](); }
           , schedule: function(schedulee) { 
               vainers.unshift(() => { vainers.shift(); return schedulee(); });
               return this;
           }
    };
} };

var viewport = { on: (bearer) => {

    var write = (text, separator) => {
                    bearer.innerHTML = bearer.innerHTML + separator + text;
    };
    
    return { message: (content) => { write(content, "<br>"); }
           , text: (content) => { write(content, "&nbsp;"); }
           , clear: () => { bearer.innerHTML = ""; }
} } };

function load() { receiver.load(); }

function query() { receiver.click(); }

var receiver = (function() {
    
    var dynamic = void(0);
    var display = void(0);
    var stack = vainer.supply(() => {
        var node = dynamic.element();
        setTimeout(() => { 
                       display.message("element of class dynamic exists: "
                                        + (node != null)); }
                  , displayDelayMillis);
        nextMessage();
    });
    var displayDelayMillis = 1000;
    var nextMessage = (delayMillis) => { 
                          setTimeout(() => { display.message("try clicking"); }
                                    , delayMillis || 2 * displayDelayMillis); };
    
    return { load: () => {
                 display = viewport.on(element.of(".viewport"));

                 dynamic = buffered.source(() => {
                     display.message("querying document for element of class dynamic...");
                     return element.of(".dynamic");
                 });

                 nextMessage(displayDelayMillis);
                 stack.schedule(() => { 
                     dynamic.reset();
                     var node = dynamic.element();
                     setTimeout(() => {
                                    display.message("element of class dynamic exists: " 
                                                     + (node != null));
                               }
                               , displayDelayMillis);
                     nextMessage();
                 });
                 stack.schedule(() => {
                     var div = document.createElement("div");
                     div.className = "dynamic";
                     element.of(".field").appendChild(div);
                     setTimeout(() => {
                                    display.message("element of class dynamic added");
                               }
                               , displayDelayMillis/4);
                     nextMessage(displayDelayMillis);
                 });
                 stack.schedule(() => { 
                     var node = dynamic.element();
                     setTimeout(() => {
                                    display.message("element of class dynamic exists: " 
                                                     + (node != null));
                               }
                               , displayDelayMillis);
                     nextMessage();
                 });
             }
           , click: () => { stack.shift(); }
    };
})();
</script>
</head>
<body onload="load()" onclick="query()" >
    <div class="field"></div>
    <div class="display">
        <div class="viewport">
        </div>
    </div>
</body>
</html>
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I'd format this with Prettier--it's difficult to read as is. Isn't this a bit premature optimization? Most modern SPA page documents change all the time, invalidating the cache. And if it's not an SPA page, then there's likely no need for any optimization. \$\endgroup\$
    – ggorlen
    Commented Jun 28 at 3:36
  • 1
    \$\begingroup\$ Agreed with @ggorlen. The code formatting is so incomprehensible that it is an active barrier to reviewing this code. \$\endgroup\$ Commented Jul 17 at 14:50

1 Answer 1

7
\$\begingroup\$

First of all, I'd advise you to format your code using Prettier to make it more readable, and then to add comments to describe the actions of your various methods.

I'd avoid using var as much as possible in favor of let (which has a block-level scope) and const (for variables that are not reassigned).

On the following lines, you use the void operator, which results to evaluating 0 and returning undefined. I don't know if this has any advantage, but you might as well declare your variable without a value:

var dynamic = void(0);
var display = void(0);
// you can do...
let dynamic, display;

Finally, in terms of code structure, I'd prefer to use the Classes introduced in ES6 (rather than object properties returning objects) to make things clearer and more maintainable.

// var stack = vainer.supply(...)
var vainer = {
    supply: (purpose) => {
        var vain = purpose ? () => purpose() : () => false;
        var vainers = [vain];

        return {
            shift: function () {
                return vainers[0]();
            },
            schedule: function (schedulee) {
                vainers.unshift(() => {
                    vainers.shift();
                    return schedulee();
                });
                return this;
            },
        };
    },
};

// const stack = new Vainer(...)
class Vainer{
    // ex. supply
    constructor(purpose){
        this.vain = purpose ? () => purpose() : () => false;
        this.vainers = [this.vain]
    }
    shift() {
        return this.vainers[0]();
    }
    schedule(schedulee) {
        this.vainers.unshift(() => {
            this.vainers.shift();
            return schedulee();
        });
        return this;
    }
}

I don't know if this buffer is a "necessary" optimization, as the use cases seem rather limited, but it's certainly a good exercise!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.