使用vanilla Javascript ES6的链延迟功能

问题描述 投票:3回答:2

你如何像jQuery库一样实现延迟? - 我知道这个问题被问过很多次,但是没有看到有人使用async/await或ES6文体来实现它。如果你有想法,请告诉我

//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()
javascript
2个回答
2
投票

您可能根本不需要promises或async/await,我认为您可以创建一个拦截后续调用的Proxy对象。

这个想法是,当调用.delay(duration)时,它将返回一个代理对象而不是类实例。此代理对象将拦截方法调用,为duration设置超时,然后使用原始类实例调用该方法。

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>

1
投票

您可以维护仍在所选元素上执行的函数队列。这样,您可以允许链中的多个延迟,并允许客户端停止操作。

代理可用于“装饰”有意义延迟的方法,以便它们可以放入队列而不是在计时器仍处于活动状态时执行。

这是如何看起来:

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>
© www.soinside.com 2019 - 2024. All rights reserved.