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();
精彩评论