ดำเนินการฟังก์ชัน setInterval โดยไม่ชักช้าในครั้งแรก


341

มันมีวิธีการกำหนดค่าsetIntervalวิธีการจาวาสคริปต์เพื่อดำเนินการวิธีการทันทีแล้วดำเนินการกับตัวจับเวลา


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สามารถมาถึงกันได้ทันทีโดยไม่ล่าช้า
Alnitak

8
ควรมีการทดสอบบางอย่างที่ไม่อนุญาตให้คุณโพสต์บนสแต็กเมื่อคุณเหนื่อย
James

3
ฉันจะหยุดฟังก์ชั่นได้อย่างไรถ้าฉันใช้setTimeoutวิธีนี้?
Gaurav Bhor

6
@DaveMunger ไม่เพราะมันไม่ใช่แบบเรียกซ้ำ - เป็นเพียง "หลอกซ้ำ" การเรียก "แบบเรียกซ้ำ" จะไม่เกิดขึ้นจนกว่าเบราว์เซอร์จะกลับไปที่ลูปเหตุการณ์ ณ จุดนี้สแต็คการโทรจะถูกเปิดออกอย่างสมบูรณ์
Alnitak

210

ฉันไม่แน่ใจว่าฉันเข้าใจคุณถูกต้องหรือไม่ แต่คุณสามารถทำสิ่งนี้ได้อย่างง่ายดาย:

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

เห็นได้ชัดว่ามีหลายวิธีในการทำเช่นนี้ แต่นั่นเป็นวิธีที่กระชับที่สุดที่ฉันสามารถนึกได้


16
นี่เป็นคำตอบที่ยอดเยี่ยมเพราะมันเป็นฟังก์ชั่นที่ตั้งชื่อซึ่งจะดำเนินการทันทีและส่งคืนตัวเอง สิ่งที่ฉันกำลังมองหา
jmort253

41
ทางออกที่ยอดเยี่ยม แต่น่าจะทำให้คนอ่านสับสนในอนาคต
Deepak Joy

4
คุณไม่จำเป็นต้องตั้งชื่อ 'สวัสดี' คุณสามารถส่งคืนอาร์กิวเมนต์
clee

10
@JochenJung arguments.calleeไม่สามารถใช้ได้ในโหมดเข้มงวด ES5
Alnitak

3
นี่คือรหัส Javascript ที่จะสร้างความสับสนให้กับโปรแกรมเมอร์ JS ส่วนใหญ่ที่มาจากภาษาอื่น เขียนสิ่งต่าง ๆ เช่นนี้ถ้า บริษัท ของคุณไม่มีบุคลากร JS จำนวนมากและคุณต้องการความมั่นคงในงาน
Ian

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);ฟังก์ชันแรกจะถูกเรียกหลังจากรอบการประมวลผลในปัจจุบันซึ่งเป็นทันทีเช่นกัน ข้อผิดพลาดเพิ่มเติมได้รับการพิสูจน์
Pensan

8

คุณสามารถสรุปsetInterval()ฟังก์ชั่นที่แสดงพฤติกรรมดังกล่าวได้:

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

... จากนั้นใช้แบบนี้:

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

5
ฉันคิดว่าคุณควรคืนสิ่งที่ setInterval ส่งคืน นี่เป็นเพราะไม่เช่นนั้นคุณไม่สามารถใช้ clearInterval
Henrik R

5

นี่คือเสื้อคลุมที่จะสวยถ้าคุณต้องการ:

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

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

ตั้งค่าอาร์กิวเมนต์ที่สามของ setInterval เป็นจริงและมันจะทำงานเป็นครั้งแรกทันทีหลังจากเรียก setInterval:

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

หรือละเว้นอาร์กิวเมนต์ที่สามและจะรักษาลักษณะการทำงานดั้งเดิมไว้:

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

เบราว์เซอร์บางตัวสนับสนุนอาร์กิวเมนต์เพิ่มเติมสำหรับ setInterval ซึ่ง wrapper นี้ไม่ได้คำนึงถึง ฉันคิดว่ามันไม่ค่อยใช้ แต่จำไว้ว่าถ้าคุณต้องการมัน


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)

นอกจากนี้คุณยังสามารถส่งผ่าน_timerไปยัง foo ถ้าคุณต้องการclearInterval(_timer)ในสภาพที่แน่นอน

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

คุณสามารถอธิบายสิ่งนี้ในเชิงลึกได้มากกว่านี้หรือไม่?
Polyducks

คุณตั้งค่าตัวจับเวลาจากการโทรครั้งที่สองเป็นครั้งแรกที่คุณเรียก fn โดยตรง
Jayant Varshney

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
นั่นคือสิ่งที่คำตอบที่ยอมรับจะทำในสองสามบรรทัดแรก คำตอบนี้เพิ่มอะไรใหม่ได้อย่างไร
Dan Dascalescu

คำตอบที่ได้รับการยอมรับจะบอกว่าอย่าทำเช่นนี้ การตอบกลับของฉันสำหรับคำตอบที่ได้รับการยอมรับอธิบายว่าทำไมมันยังเป็นวิธีที่ถูกต้อง OP ขอเฉพาะสำหรับการเรียกใช้ฟังก์ชันของ setInterval ในการโทรครั้งแรก แต่คำตอบที่ยอมรับจะเปลี่ยนกลับเพื่อพูดคุยเกี่ยวกับประโยชน์ของ setTimeout
Polyducks

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 }));


นี่เป็นสิ่งที่สร้างขึ้นอย่างมากมาย
Polyducks

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)

ซึ่งจะไม่เรียกใช้ แต่กำหนดเวลาเท่านั้น


คุณได้สิ่งนั้นมาจากไหน
Ken Sharp

2
ใช้งานได้ก็ต่อเมื่อmyFunction()กลับมาเอง แทนที่จะแก้ไขแต่ละฟังก์ชั่นที่จะเรียกใช้โดยsetIntervalมันเป็นวิธีที่ดีกว่าในการห่อsetIntervalเมื่อคำตอบอื่น ๆ กำลังเสนอ
Jens Wirth
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.