Выполнить settimeout рано
Я использую debouncing для выполнения событий после тайм-аута, используя settimeout. Проблема, с которой я столкнулся, заключается в том, что другие события javascript ожидают, что эти события происходят синхронно. Так как они теперь выполняются после истечения времени ожидания, я хотел бы иметь возможность преждевременно инициировать их с помощью других событий javascript (чтобы те события, которые требуют их, не потерпели неудачу).
Anywom, если я сделаю что-то вроде:
timeout = setTimeout(function() { alert('hi'); }, 10000);
и я хочу, чтобы это произошло до того, как пройдет 10 секунд, как я могу это сделать?
Решение может включать jquery, если это необходимо. Спасибо!
Редактировать: возможно ли сделать это только с помощью доступа к объекту тайм-аута?
5 ответов
Итак, если вы делаете что-либо, вы задерживаете свою собственную функцию:
function sayHi() {
alert('hi');
}
Вы можете использовать тайм-аут и обычный вызов функции:
var timeout = setTimeout(sayHi, 10000); // say hi after 10 seconds
Или вызвать его до истечения времени ожидания, просто вызывайте функцию всякий раз, когда вам нужно:
sayHi();
Я на правильном пути здесь? Если вам нужно отменить тайм-аут, позвоните clearTimeout()
на ваше timeout
переменная.
if (timeout)
clearTimeout(timeout);
Вы не можете отслеживать это со стандартом setTimeout
, но Javascript позволяет расширять возможности по вашему желанию.
Например, вы могли бы иметь свой собственный улучшенный setTimeout
:
var _setTimeout = window.setTimeout;
var timeouts = [];
window.setTimeout = function(fn, ms) {
var id = _setTimeout(fn, ms);
timeouts[id] = fn;
return id;
};
window.premature = function(id) {
var fn = timeouts[id];
if (fn) {
clearTimeout(id);
if (fn instanceof String) {
eval(fn);
} else {
fn()
}
}
};
function printDate(str) {
$("body").append("<p>" + str + ". " + new Date() + "</p>");
}
$(function() {
var id1 = setTimeout(function() { printDate("id1"); }, 10000);
var id2 = setTimeout(function() { printDate("id2"); }, 10000);
printDate("first one");
// just to demonstrate that the string version works too
setTimeout("window.premature(" + id1 +")", 5000);
});
Вы можете увидеть это в действии на jsFiddle
Обратите внимание, что этот простой взлом не учитывает очистку использованных идентификаторов, когда тайм-ауты действительно происходят, а просто показывает, что вы можете делать такие вещи в Javascript, если вам это действительно нужно.
полифилл раствор
Вот некоторый JavaScript, который я обновил из предыдущего проекта, теперь он расширен методами триггера и обновления; оно похоже на решение Яна Викхольма (+1), но немного более полное, с учетом очистки, передачи аргументов и предотвращения eval
если необходимо:
(function(keep){
/// a few things to remember
keep.setTimeout = window.setTimeout;
keep.clearTimeout = window.clearTimeout;
keep.TO = function(){};
keep.list = {};
keep.settings = {
eval: false /// set this to true if you wish to support string timeouts
};
/**
* Quick check function to prevent eval
*/
keep.checkParam = function( param ){
if ( !keep.settings.eval && typeof param == 'string' ) {
throw new Error('setTimeout blocked evaluation of string, ' +
'use a function instead.');
return false;
}
else if ( param ) {
return true;
}
};
/**
* Simple function constructor to avoid trapping unwanted references
*/
keep.makeFunction = function(data){
return function(args){
/// copy our args array
args = data.args.slice();
/// do we allow eval?
if ( keep.settings.eval ) {
/// if so, reuse setTimeout for it's abilities
args[0] = data.param; /// use the original param
args[1] = 0; /// trigger immediately
keep.setTimeout.apply( window, args );
}
// more secure, assume dealing with function -- not string
else if ( keep.checkParam( data.param ) && data.param.apply ) {
data.param.apply( window, args.slice(2) );
}
else {
throw new Error('unsupported param for setTimeout' +
' i.e. non-function used.');
}
/// clear our storage of this tid
window.clearTimeout( data.tid );
};
};
/**
* Sets timeouts just like you would expect
*/
window.setTimeout = function( param, timeout ){
if ( keep.checkParam( param ) ) {
var tid, data;
/// support passing a timeout object as param
if ( param instanceof keep.TO ) {
data = param;
data.args[1] = data.timeout;
}
else {
/// create an object to store the timeout info
data = new keep.TO();
data.func = keep.makeFunction(data);
data.param = param;
data.timeout = timeout;
data.args = Array.prototype.slice.call(arguments,0);
data.args[0] = data.func;
}
data.tid = keep.setTimeout.apply( window, data.args );
keep.list[data.tid] = data;
/// enhance the returned number to support .clear, .trigger and .update
tid = new Number(data.tid);
tid.clear = window.clearTimeout;
tid.trigger = window.triggerTimeout;
tid.update = window.updateTimeout;
return tid;
}
};
/**
* Clearing timeouts since 2013
*/
window.clearTimeout = function( tid ){
if ( this instanceof Number ) {
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
delete keep.list[tid];
keep.clearTimeout.call(window, tid);
}
};
/**
* Returns the internal timeout storage object
*/
window.getTimeout = function( tid ){
var obj;
if ( (obj = keep.list[tid]) ) {
return obj;
}
};
/**
* Clears and fires a timeout before it's outed time
*/
window.triggerTimeout = function( tid ){
if ( this instanceof Number ) {
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
window.clearTimeout(tid);
obj.func.call(window);
}
else {
throw new Error('No Timeout found to trigger for ID '+ tid);
}
};
/**
* Clears and recreates an existing timeout, returns a new timeout id.
*/
window.updateTimeout = function( tid, timeout ){
if ( this instanceof Number ) {
if ( arguments.length == 1 ) {
timeout = tid;
}
tid = 0 + this;
}
var obj;
if ( (obj = window.getTimeout(tid)) ) {
obj.timeout = timeout;
window.clearTimeout(tid);
return window.setTimeout(obj);
}
else {
throw new Error('No Timeout found to update for ID ' + tid);
}
};
/**
* Utility function to tidy up
*/
window.clearAllTimeouts = function(){
for ( var i in keep.list ) {
window.clearTimeout(i);
};
};
/// Tidy up
window.onunload = (function(previous){
return function(){
window.clearAllTimeouts();
keep.list = {};
previous && previous.call(window);
};
}(window.onunload));
})({});
включают
Просто поместите вышеупомянутое в файл js и включите в свою страницу с помощью обычного тега script, код не должен вызываться каким-либо образом:
<script src="timeouts.js"></script>
использование
Очевидно, это следует использовать как обычный setTimeout
call, однако теперь у вас есть дополнительные методы, которые должны обеспечить большую гибкость.
var tid = setTimeout( function(){ alert('OK Computer') }, 2000 );
Например, вы можете отменить оригинал и заставить тайм-аут срабатывать раньше:
setTimeout( function(){ triggerTimeout( tid ); }, 500 );
Или вы можете обновить тайм-аут (убедитесь, что мы помним новый возвращенный tid):
setTimeout( function(){ tid = updateTimeout( tid, 5000 ); }, 500 );
Вы также можете сделать обычное:
setTimeout( function(){ clearTimeout( tid ); }, 1000 );
Каждый из этих методов также доступен через tid
сам:
setTimeout( function(){ tid.trigger(); }, 1000 );
setTimeout( function(){ tid.update( 5000 ); }, 1000 );
setTimeout( function(){ tid.clear(); }, 1000 );
По умолчанию этот код запрещает использование setTimeout
со строковым параметром, главным образом потому, что это гораздо лучший стиль кодирования для передачи функций, а не строк. Чтобы изменить это, вы можете переключить следующий параметр в true:
keep.settings = {
eval: true
};
Однако это не рекомендуется.
Есть также дополнительное преимущество в том, что eval отключен, потому что код будет использовать обычный вызов функции для запуска тайм-аута, т.е. .apply()
, Это означает, что независимо от того, какой браузер вы используете, вы можете передавать аргументы в функцию timeout через setTimeout - обычно это не то, на что вы можете положиться в кросс-браузере. например:
setTimeout( function(a){ alert(a) }, 2000, 'Hello World' );
Просто выведите функцию и дайте ей имя:
function handler(){
alert('hi');
}
timeout = setTimeout(handler, 10000);
тогда вы можете позвонить в другие места с handler();
Использование clearTimeout
и перенести на более раннее время.