Thực hiện chức năng setInterval không chậm trễ lần đầu tiên


339

Đó là cách để cấu hình setIntervalphương thức javascript để thực thi phương thức ngay lập tức và sau đó thực thi với bộ đếm thời gian


4
Không phải tự nhiên mặc dù. Bạn có thể thử gọi functionmột lần và sau đó thực hiệnsetInterval()
Mrchief

Câu trả lời:


519

Đơn giản nhất là chỉ cần gọi hàm trực tiếp lần đầu tiên:

foo();
setInterval(foo, delay);

Tuy nhiên, có những lý do chính đáng để tránh setInterval- đặc biệt trong một số trường hợp, toàn bộ các setIntervalsự kiện có thể đến ngay sau đó mà không có bất kỳ sự chậm trễ nào. Một lý do khác là nếu bạn muốn dừng vòng lặp, bạn phải gọi một cách rõ ràng clearInterval, điều đó có nghĩa là bạn phải nhớ tay cầm được trả về từ setIntervalcuộc gọi ban đầu .

Vì vậy, một phương pháp thay thế là footự kích hoạt các cuộc gọi tiếp theo bằng cách sử dụng setTimeout:

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

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

// start the cycle
foo();

Điều này đảm bảo rằng có ít nhất một khoảng delaygiữa các cuộc gọi. Nó cũng giúp việc hủy vòng lặp dễ dàng hơn nếu được yêu cầu - bạn chỉ không gọi setTimeoutkhi đạt đến điều kiện chấm dứt vòng lặp.

Tốt hơn nữa, bạn có thể gói tất cả trong một biểu thức hàm được gọi ngay lập tức để tạo ra hàm, sau đó tự gọi lại như trên và tự động bắt đầu vòng lặp:

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

trong đó xác định hàm và bắt đầu chu trình tất cả trong một lần.


5
Tại sao bạn thích setTimeout?
Sanghyun Lee

19
@Sangdol vì nó đảm bảo rằng các sự kiện hẹn giờ không "chồng" nếu chúng chưa được xử lý. Trong một số trường hợp, toàn bộ các setIntervalsự kiện có thể đến ngay lập tức với nhau mà không có bất kỳ sự chậm trễ nào.
Alnitak

8
Cần có một số loại thử nghiệm không cho phép bạn đăng lên stack khi bạn cảm thấy mệt mỏi
James

3
Làm cách nào để dừng chức năng nếu tôi sử dụng setTimeoutphương thức?
Gaurav Bhor

6
@DaveMunger không, vì nó không thực sự đệ quy - nó chỉ "giả đệ quy". Các cuộc gọi "đệ quy" không xảy ra cho đến khi trình duyệt quay trở lại vòng lặp sự kiện, tại thời điểm đó, ngăn xếp cuộc gọi đã hoàn toàn không được thực hiện.
Alnitak

209

Tôi không chắc là tôi có hiểu đúng về bạn không, nhưng bạn có thể dễ dàng làm điều gì đó như thế này:

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

Rõ ràng có nhiều cách để làm điều này, nhưng đó là cách ngắn gọn nhất mà tôi có thể nghĩ ra.


16
Đây là một câu trả lời thú vị bởi vì nó là một hàm có tên thực thi ngay lập tức và cũng trả về chính nó. Chính xác những gì tôi đang tìm kiếm.
jmort253

41
Chắc chắn là một giải pháp tuyệt vời, nhưng có khả năng gây nhầm lẫn cho những người đọc trong tương lai.
Deepak Joy

4
Bạn không cần đặt cho nó một cái tên 'xin chào'. Thay vào đó, bạn có thể trả về argument.callee và có cùng chức năng ẩn danh
JochenJung

10
@JochenJung arguments.calleekhông khả dụng trong chế độ nghiêm ngặt ES5
Alnitak

3
Đây là loại mã Javascript sẽ gây nhầm lẫn cho hầu hết các lập trình viên JS mới đến từ các ngôn ngữ khác. Viết một loạt các công cụ như thế này nếu công ty của bạn không có nhiều nhân viên JS và bạn muốn bảo mật công việc.
Ian

13

Tôi vấp phải câu hỏi này do cùng một vấn đề nhưng không có câu trả lời nào có ích nếu bạn cần cư xử chính xác như thế setInterval()nhưng với sự khác biệt duy nhất là hàm được gọi ngay từ đầu.

Đây là giải pháp của tôi cho vấn đề này:

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

Ưu điểm của giải pháp này:

  • sử dụng mã hiện setIntervalcó có thể dễ dàng được điều chỉnh bằng cách thay thế
  • làm việc ở chế độ nghiêm ngặt
  • nó hoạt động với các chức năng được đặt tên và đóng
  • bạn vẫn có thể sử dụng giá trị trả về và chuyển nó về clearInterval()sau

Thí dụ:

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

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

Để táo tợn, nếu bạn thực sự cần sử dụng setIntervalthì bạn cũng có thể thay thế bản gốc setInterval. Do đó, không cần thay đổi mã khi thêm mã này trước mã hiện tại của bạn:

var setIntervalOrig = setInterval;

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

Tuy nhiên, tất cả các lợi thế như được liệt kê ở trên áp dụng ở đây nhưng không cần thay thế.


Tôi thích giải pháp này hơn setTimeoutgiải pháp vì nó trả về một setIntervalđối tượng. Để tránh gọi các hàm có thể nằm sau mã và do đó không được xác định tại thời điểm hiện tại, tôi gói lệnh gọi hàm đầu tiên trong một setTimeouthàm như thế này: setTimeout(function(){ func();},0);Hàm đầu tiên sau đó được gọi sau chu trình xử lý hiện tại, cũng ngay lập tức , nhưng là nhiều lỗi đã được chứng minh.
penan

8

Bạn có thể gói setInterval()trong một hàm cung cấp hành vi đó:

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

... Sau đó sử dụng nó như thế này:

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

5
Tôi nghĩ bạn nên trả lại những gì setInterval trả về. Điều này là do nếu không bạn không thể sử dụng ClearInterval.
Henrik R

5

Đây là một trình bao bọc để làm đẹp nó nếu bạn cần nó:

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

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

Đặt đối số thứ ba của setInterval thành true và nó sẽ chạy lần đầu tiên ngay sau khi gọi setInterval:

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

Hoặc bỏ qua đối số thứ ba và nó sẽ giữ lại hành vi ban đầu của nó:

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

Một số trình duyệt hỗ trợ các đối số bổ sung cho setInterval mà trình bao bọc này không tính đến; Tôi nghĩ rằng chúng hiếm khi được sử dụng, nhưng hãy ghi nhớ nếu bạn cần chúng.


9
Ghi đè các chức năng trình duyệt riêng là rất tệ vì nó có thể phá vỡ mã cùng tồn tại khác khi thông số kỹ thuật thay đổi. Trên thực tế, setInterval hiện có nhiều tham số hơn: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/
Kẻ

2

Đối với ai đó cần phải mang bên ngoài thisbên trong như thể đó là một chức năng mũi tên.

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

Nếu việc sản xuất rác ở trên làm phiền bạn, bạn có thể đóng cửa thay thế.

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

Hoặc có thể xem xét sử dụng các @autobindtrang trí tùy thuộc vào mã của bạn.


1

Tôi sẽ đề nghị gọi các hàm theo trình tự sau

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

Bạn cũng có thể chuyển _timercho foo, nếu bạn muốn với clearInterval(_timer)một điều kiện nhất định

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

Bạn có thể giải thích điều này sâu hơn?
Polyducks

bạn đặt bộ hẹn giờ từ cuộc gọi thứ hai, lần đầu tiên bạn chỉ cần gọi trực tiếp cho fn.
Jayant Varshney

1

Đây là một phiên bản đơn giản cho người mới mà không có sự lộn xộn xung quanh. Nó chỉ khai báo hàm, gọi nó, sau đó bắt đầu khoảng. Đó là nó.

//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
Đó chính xác là những gì câu trả lời được chấp nhận làm trong vài dòng đầu tiên. Làm thế nào để câu trả lời này thêm bất cứ điều gì mới?
Dan Dascalescu

Câu trả lời được chấp nhận tiếp tục nói không làm điều này. Phản hồi của tôi đối với câu trả lời được chấp nhận giải thích tại sao nó vẫn là một phương pháp hợp lệ. OP đặc biệt yêu cầu gọi chức năng của setInterval trong cuộc gọi đầu tiên nhưng câu trả lời được chấp nhận chuyển hướng để nói về lợi ích của setTimeout.
Polyducks

0

Để giải quyết vấn đề này, tôi chạy chức năng lần đầu tiên sau khi trang được tải.

function foo(){ ... }

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

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

0

Có một gói npm tiện lợi được gọi là FirstInterval (công bố đầy đủ, nó là của tôi).

Nhiều ví dụ ở đây không bao gồm xử lý tham số và thay đổi hành vi mặc định của setIntervalbất kỳ dự án lớn nào là xấu. Từ các tài liệu:

Mẫu này

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

giống hệt với

firstInterval(callback, 1000, p1, p2);

Nếu bạn là trường học cũ trong trình duyệt và không muốn phụ thuộc, thì đó là cách dễ dàng cắt và dán từ mã.


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

Ok điều này rất phức tạp, vì vậy, hãy để tôi nói đơn giản hơn:

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

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


Đây là quá nhiều thiết kế.
Polyducks

0

Bạn có thể đặt thời gian trễ ban đầu rất nhỏ (ví dụ 100) và đặt thời gian trễ mong muốn trong hàm:

var delay = 100;

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


-2

Có vấn đề với cuộc gọi không đồng bộ ngay lập tức của chức năng của bạn, bởi vì setTimeout / setInterval tiêu chuẩn có thời gian chờ tối thiểu khoảng vài mili giây ngay cả khi bạn trực tiếp đặt nó thành 0. Nó gây ra bởi một công việc cụ thể của trình duyệt.

Một ví dụ về mã với độ trễ bằng không THỰC SỰ hoạt động trong Chrome, Safari, Opera

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

Bạn có thể tìm thêm thông tin ở đây

Và sau cuộc gọi thủ công đầu tiên, bạn có thể tạo một khoảng với chức năng của mình.


-10

thực sự nhanh nhất là làm

interval = setInterval(myFunction(),45000)

cái này sẽ gọi chức năng của tôi, và sau đó sẽ thực hiện nó sau mỗi 45 giây, khác với làm

interval = setInterval(myfunction, 45000)

mà sẽ không gọi nó, nhưng chỉ lên lịch


bạn đã nhận được ở đâu đó từ đâu?
Ken Sharp

2
Điều này chỉ hoạt động nếu myFunction()không trở lại chính nó. Thay vào đó, sửa đổi từng chức năng được gọi bởi setIntervalnó là một cách tiếp cận tốt hơn để bọc setIntervalmột lần giống như các câu trả lời khác đang đề xuất.
Jens Wirth
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.