3

How do you implement delay like the jQuery Library? - I know this question has been asked so many times, but haven't seen anyone implement it using async/await or ES6 stylistics. let me know if you have ideas

//create a jquery like library
class DOM {
    constructor(selector){
        this.elements = [];
        if(!selector){
            return;
        } else if(selector === 'document' || selector === 'window'){
            this.elements = [selector];
        } else {
            this.elements = Array.from(document.querySelectorAll(selector));
        }
    }

    on(){
        console.log('on');
        return this;
    }

    get(){
        console.log('get');
        return this;
    }


    delay(ms, callback){
        //how to implement this? how to chain result of callback onto next chain?
        console.log('delay');
        const promise = Promise.resolve();
        return promise.then(function(resolve) {
            setTimeout(function(){
                resolve(this);
            }, ms);
        });
    }
}

const $ = function(selector) {
    return new DOM(selector);
}       

$('document').on().delay(10000).get()
4
  • What do you expect this code to do? What should console.log output? Or did you expect that console.log itself would be delayed?? Commented Apr 26, 2019 at 5:15
  • it should print out on...after 10 seconds...get. I deleted that text, what I meant is when used it should not be delay(ms).then(next) rather delay(ms).next() Commented Apr 26, 2019 at 5:19
  • Possible duplicate of Delay to next function in method chain Commented Apr 26, 2019 at 5:31
  • Also see the accepted answer to this question. Commented Apr 26, 2019 at 5:34

2 Answers 2

3

You probably don't need promises or async/await at all, I think you can create a Proxy object that intercepts subsequent call.

The idea is that, when .delay(duration) is called, it'll return a proxy object instead of the class instance. This proxy object will intercept a method call, set time out for duration, then call the method with the original class instance.

class J {
  constructor(selector) {
    this.$element = document.querySelector(selector)
  }
  delay(duration) {
    const proxy = new Proxy(this, {
      get: (target, prop) => {
        const f = target[prop]
        return (...args) => {
          setTimeout(() => {
            return f.apply(target, [...args])
          }, duration)

          // return the class instance again, so subsequent call & delay still works
          return this
        }
      }
    })
    return proxy
  }
  text(content) {
    this.$element.textContent = content
    return this
  }
}

const $ = selector => new J(selector)

$('#test').text('hello').delay(1000).text('world')
<div id="test"></div>

Sign up to request clarification or add additional context in comments.

Comments

2

You could maintain a queue of functions still to execute on the selected element(s). That way you can allow multiple delays in the chain and also allow the client to stop the action.

A proxy can be used to "decorate" the methods which make sense to be delayed, so that they can be put in the queue instead of executed whenever a timer is still active.

Here is how that could look:

class DOM {
    constructor(selector) {
        this.elements = typeof selector === "object" ? [selector]
            : selector === 'document' || selector === 'window' ? [document] 
            : Array.from(document.querySelectorAll(selector));
        this.delayed = false;
        this.queue = [];
        const proxy = new Proxy(this, {
            get(obj, prop) {
                return !["css","show","hide","delay"].includes(prop) || !obj.delayed ? obj[prop]
                    : function (...args) {
                        obj.queue.push(() => proxy[prop](...args));
                        return this;
                    }
            }
        });
        return proxy;
    }
    each(cb) {
        this.elements.forEach(cb);
        return this;
    }
    css(name, value) {
        return this.each(elem => elem.style[name] = value);
    }
    show() {
        return this.css("display", "");
    }
    hide() {
        return this.css("display", "none");
    }
    on(eventType, cb) {
        return this.each(elem => elem.addEventListener(eventType, cb.bind(elem)));
    }
    delay(ms) {
        this.delayed = true;
        setTimeout(() => {
            this.delayed = false;
            while (this.queue.length && !this.delayed) this.queue.shift()();
        }, ms);
        return this;
    }
    stop() {
        this.queue.length = 0;
        return this;
    }
}

const $ = selector => new DOM(selector);    

const $span = $('#demo').hide();
for (let i = 0; i < 100; i++) {
    $span.delay(500).show()
         .delay(500).css("color", "red")
         .delay(500).css("color", "blue")
         .delay(500).hide();
}
$("#stop").on("click", function () {
    $span.stop();
    $(this).hide();
});
<div>This is a <span id="demo">colorful </span>demo</div>
<button id="stop">Stop</button>

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.