开发者

Force setTimeout to fire its payload earlier than originally set

I have developed a small bit of presentation software which consists of slides and assets for each slide. When a slide is rendered all of its assets are looped through and rendered after a delay using开发者_StackOverflow中文版 the setTimeout method. looks sweet...

Yaaay!, requirements have changed, the presentation is now required to act like a PowerPoint slideshow, the mouse click event will cause the next asset to be rendered to the page immediately.

My question is; is there a way to cause my timeout to fire immediately? I can get and store in a stack the timeoutid as it is returned when the timeout is created. The only other option would be to cancel the timeout and then recreate the element, which is a lot more processing then i want to do, and I dont feel like refactoring my code too much.

Any ideas?


You could wrap it in a closure like this:

function createTimeout(timeoutHandler, delay) {
    var timeoutId;
    timeoutId = setTimeout(timeoutHandler, delay);
    return {
        clear: function() {
            clearTimeout(timeoutId);
        },
        trigger: function() {
            clearTimeout(timeoutId);
            return timeoutHandler();
        }
    };
}

var a = new Date();

var timeout = createTimeout(function() { console.log(a); }, 1000);
// timeout.clear();
timeout.trigger();

Updated (modern js):

let newTimeout = (handler, delay) => {
    let id = setTimeout(handler, delay), clear = clearTimeout.bind(null, id);
    return {id, clear, trigger: () => (clear(), handler())};
};

let timeout = newTimeout(() => console.log(new Date()), 1000);
// timeout.clear();
timeout.trigger();


If you set the timer like this:

var timer1 = window.setTimeout(mainFunction,500)

call it immediately by doing this:

window.clearTimeout(timer1)
mainFunction()

The key is to separate the function from the timer.


Short answer is No. You cannot do what you are asking, unless you keep a list of the methods to be fired. You could then cancel the timer and trigger the corresponding method.


I just came across this question and ended up writing a convenience class to handle this (using es6, so you’ll need to transpile it for backwards compatibility):

class Animations {
  constructor() {
    this.queue = [];
    this.nextId = 0;
  }
  add(fn, delay) {
    let id = this.nextId++;
    let timeout = window.setTimeout(() => {
      this.queue = this.queue.filter(d => d.id !== id);
      fn();
    }, delay);
    this.queue.push({ id, fn, timeout });
    return id;
  }
  flush() {
    this.queue.forEach(d => {
      window.clearTimeout(d.timeout);
      d.fn();
    });
    this.queue = [];
  }
}

export default Animations;

Then instead of using setTimeout, I use one instance of the Animations class to manage all the state and let me setTimeouts via the .add(fn, delay) method, but I can force pending ones to run via .flush().

import Animations from './animations';

let anims = new Animations();

anims.add(() => {
  someElt.classList.add('some-class');
}, 1000);

// then, whenever you want to force it execute immediately
anims.flush();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜