Виконайте функцію setInterval без зволікання вперше


341

Там є спосіб налаштувати setIntervalметод javascript, щоб виконати метод негайно, а потім виконати за допомогою таймера


4
Не рідно, хоча. Ви можете спробувати зателефонувати functionодин раз, а потім зробитиsetInterval()
Mrchief

Відповіді:


519

Найпростіше просто зателефонувати безпосередньо в функцію самостійно:

foo();
setInterval(foo, delay);

Однак є вагомі причини, яких слід уникати setInterval- зокрема, за деяких обставин, цілий набір setIntervalподій може прийти негайно один за одним без жодних затримок. Ще одна причина полягає в тому, що якщо ви хочете зупинити цикл, вам потрібно чітко зателефонувати, а clearIntervalце означає, що ви повинні запам'ятати ручку, повернуту з початкового setIntervalвиклику.

Отже, альтернативним методом є створення fooтригера для наступних дзвінків, використовуючи setTimeoutзамість цього:

function foo() {
   // do stuff
   // ...

   // and schedule a repeat
   setTimeout(foo, delay);
}

// start the cycle
foo();

Це гарантує наявність принаймні інтервалу delayміж дзвінками. Це також спрощує скасування циклу, якщо це потрібно - ви просто не дзвоните, setTimeoutколи буде досягнуто умови завершення циклу.

А ще краще, ви зможете обернути це все виразу функції, що викликається функцією, яка створює функцію, яка потім викликає себе знову, як вище, і автоматично запускає цикл:

(function foo() {
    ...
    setTimeout(foo, delay);
})();

яка визначає функцію і запускає цикл все за один раз.


5
Чому ви віддаєте перевагу setTimeout?
Sanghyun Lee

19
@Sangdol, оскільки він забезпечує, що події таймера не «стикуються», якщо їх не обробити. За деяких обставин цілий набір setIntervalподій може прийти негайно один за одним без жодної затримки.
Альнітак

8
Має бути якийсь тест, який не дає вам публікувати на стеці, коли ви втомилися
Джеймс

3
Як зупинити функцію, якщо я використовую setTimeoutметод?
Gaurav Bhor

6
@DaveMunger ні, тому що це не справді рекурсивно - це лише "псевдорекурсивна". "Рекурсивні" дзвінки не відбуваються до тих пір, поки браузер не повернеться до циклу подій, після чого стек викликів повністю розкручений.
Альнітак

210

Я не впевнений, чи правильно я вас розумію, але ви могли легко зробити щось подібне:

setInterval(function hello() {
  console.log('world');
  return hello;
}(), 5000);

Очевидно, існує будь-яка кількість способів зробити це, але це найбільш стислий спосіб, який я можу придумати.


16
Це класна відповідь, оскільки це названа функція, яка виконується негайно, а також повертається сама. Саме те, що я шукав.
jmort253

41
Безумовно, класне рішення, але, можливо, спричинить плутанину у людей, які читають у майбутньому.
Deepak Joy

4
Не потрібно давати йому ім'я "привіт". Ви можете замість цього повернути argument.callee і мати те ж саме з анонімною функцією
JochenJung

10
@JochenJung arguments.calleeнедоступний у суворому режимі
ES5

3
Це вид коду Javascript, який заплутає більшість нових програмістів JS, які прийшли з інших мов. Напишіть купу подібних матеріалів, якщо у вашій компанії немає багато персоналу JS і ви хочете забезпечити роботу.
Ян

13

Я натрапив на це питання в зв'язку з тією ж проблемою , але жоден з відповідей не допомагає , якщо вам потрібно вести себе так само , як , setInterval()але з єдиною різницею , що функція викликається безпосередньо на самому початку.

Ось моє рішення цієї проблеми:

function setIntervalImmediately(func, interval) {
  func();
  return setInterval(func, interval);
}

Перевага цього рішення:

  • існуючий код за допомогою setIntervalлегко може бути адаптований за допомогою підстановки
  • працює в суворому режимі
  • він працює з існуючими названими функціями та закриттями
  • ви все одно можете використовувати повернене значення та передати його clearInterval()пізніше

Приклад:

// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
        console.log('hello');
    }, 1000);

// clear interval after 4.5 seconds
setTimeout( _ => {
        clearInterval(myInterval);
    }, 4500);

Щоб бути сміливим, якщо вам справді потрібно користуватися, setIntervalтоді ви також можете замінити оригінал setInterval. Отже, не потрібно змінювати код при додаванні цього до наявного коду:

var setIntervalOrig = setInterval;

setInterval = function(func, interval) {
    func();
    return setIntervalOrig(func, interval);
}

Однак усі переваги, перелічені вище, застосовуються тут, але заміни не потрібно.


Я вважаю за краще це рішення над setTimeoutрішенням, оскільки воно повертає setIntervalоб'єкт. Щоб уникнути виклику функцій, які, можливо, пізніше знаходяться в коді і тому не визначені в поточний час, я запускаю перший виклик функції у setTimeoutфункцію, як ця: setTimeout(function(){ func();},0);Перша функція викликається після поточного циклу обробки, який також негайно , але є більше помилок доведено.
пенсан

8

Ви можете зафіксувати setInterval()функцію, яка забезпечує таку поведінку:

function instantGratification( fn, delay ) {
    fn();
    setInterval( fn, delay );
}

... тоді використовуйте його так:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);

5
Я думаю, ви повинні повернути те, що повертає setInterval. Це тому, що в іншому випадку ви не можете використовувати clearInterval.
Генрік Р

5

Ось обгортка, щоб її добре переконати, якщо вона вам потрібна:

(function() {
    var originalSetInterval = window.setInterval;

    window.setInterval = function(fn, delay, runImmediately) {
        if(runImmediately) fn();
        return originalSetInterval(fn, delay);
    };
})();

Встановіть третій аргумент setInterval на true і він запуститься вперше відразу після виклику setInterval:

setInterval(function() { console.log("hello world"); }, 5000, true);

Або пропустіть третій аргумент, і він збереже свою первісну поведінку:

setInterval(function() { console.log("hello world"); }, 5000);

Деякі браузери підтримують додаткові аргументи для setInterval, які ця обгортка не враховує; Я думаю, що вони рідко використовуються, але майте це на увазі, якщо вони вам потрібні.


9
Переосмислення функцій нативного браузера є жахливим, оскільки він може порушити інший співіснуючий код, коли характеристики змінюються. Насправді, setInterval наразі має більше параметрів: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/…
Аксель Костас Пена

2

Для когось потрібно піднести зовнішнє thisвсередину так, ніби це функція стрілки.

(function f() {
    this.emit("...");
    setTimeout(f.bind(this), 1000);
}).bind(this)();

Якщо вищезгадане виробництво сміття вас турбує, ви можете замість цього зробити закриття.

(that => {
    (function f() {
        that.emit("...");
        setTimeout(f, 1000);
    })();
})(this);

Або , може бути , розглянути можливість використання в @autobindдекоратора в залежності від вашого коду.


1

Я запропоную викликати функції у наступній послідовності

var _timer = setInterval(foo, delay, params);
foo(params)

Ви також можете передати " _timerfoo", якщо хочете clearInterval(_timer)за певної умови

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);

Чи можете ви пояснити це детальніше?
Полідуки

ви встановлюєте таймер під час другого дзвінка, в перший раз ви просто зателефонуєте на fn.
Джаянт Варшні

1

Ось проста версія для новачків без усіх возитися. Він просто оголошує функцію, викликає її, а потім запускає інтервал. Це воно.

//Declare your function here
function My_Function(){
  console.log("foo");
}    

//Call the function first
My_Function();

//Set the interval
var interval = window.setInterval( My_Function, 500 );


1
Саме це і приймає відповідь у перших кількох рядках. Як ця відповідь додає щось нове?
Дан Даскалеску

Прийнята відповідь продовжує говорити, що цього не робити. Моя відповідь на прийняту відповідь пояснює, чому це все-таки дійсний метод. ОП спеціально просить зателефонувати на функцію setInterval під час першого дзвінка, але прийнята відповідь не дозволяє говорити про переваги setTimeout.
Полідуки

0

Щоб вирішити цю проблему, я запускаю функцію вперше після завантаження сторінки.

function foo(){ ... }

window.onload = function() {
   foo();
};

window.setInterval(function()
{
    foo(); 
}, 5000);

0

Є зручний пакет npm під назвою firstInterval (повне розкриття, це моє).

Багато прикладів тут не включають обробку параметрів, а зміна поведінки за замовчуванням setIntervalу будь-якому великому проекті є злом. З документів:

Ця закономірність

setInterval(callback, 1000, p1, p2);
callback(p1, p2);

тотожна

firstInterval(callback, 1000, p1, p2);

Якщо ви старі школи в браузері і не хочете залежності, це просто вирізати і вставити з коду.


0

// YCombinator
function anonymous(fnc) {
  return function() {
    fnc.apply(fnc, arguments);
    return fnc;
  }
}

// Invoking the first time:
setInterval(anonymous(function() {
  console.log("bar");
})(), 4000);

// Not invoking the first time:
setInterval(anonymous(function() {
  console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
  console.log("baz");
}, 4000);

Ок, це так складно, тож дозвольте сказати простіше:

function hello(status ) {    
  console.log('world', ++status.count);
  
  return status;
}

setInterval(hello, 5 * 1000, hello({ count: 0 }));


Це значно перероблено.
Полідуки

0

Ви можете встановити дуже невеликий початковий час затримки (наприклад, 100) і встановити його бажаний час затримки в межах функції:

var delay = 100;

function foo() {
  console.log("Change initial delay-time to what you want.");
  delay = 12000;
  setTimeout(foo, delay);
}


-2

Виникла проблема з негайним асинхронним викликом вашої функції, оскільки стандартний setTimeout / setInterval має мінімальний час очікування приблизно кілька мілісекунд, навіть якщо ви безпосередньо встановите його на 0. Це викликано роботою, пов’язаною з браузером.

Приклад коду з РЕАЛЬНОЮ нульовою затримкою, який працює в Chrome, Safari, Opera

function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}

Ви можете знайти більше інформації тут

І після першого ручного дзвінка ви можете створити інтервал зі своєю функцією.


-10

насправді це найшвидше зробити

interval = setInterval(myFunction(),45000)

це зателефонує моїй функції, а потім робитиме її агайською кожні 45 секунд, що відрізняється від виконання

interval = setInterval(myfunction, 45000)

яка не називатиме її, а запланувати лише її


Звідки ти це взяв?
Кен Шарп

2
Це працює лише в тому випадку, якщо все-таки myFunction()повернеться. Замість того, щоб змінювати кожну функцію, яку setIntervalвона викликає, краще підходити до завершення setIntervalодного разу, як пропонують інші відповіді.
Йенс Вірт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.