setTimeout hay setInterval?


763

Theo như tôi có thể nói, hai đoạn javascript này hoạt động theo cùng một cách:

Tùy chọn A:

function myTimeoutFunction()
{
    doStuff();
    setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

Tùy chọn B:

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Có sự khác biệt nào giữa việc sử dụng setTimeoutsetInterval không?


6
Nếu bạn muốn có một số chi tiết tốt về cách các bộ tính giờ trong JS hoạt động, John Resig đã viết một bài viết hay về chủ đề này
Rafael

9
Ngoài ra còn có sự khác biệt rõ ràng rằng setTimeout yêu cầu dòng mã bổ sung để duy trì sự lan truyền, điều này có nhược điểm là vấn đề bảo trì nhưng lợi ích của việc cho phép bạn thay đổi thời gian một cách dễ dàng
annakata


4
Cảm ơn @JapanPro nhưng tôi chưa bao giờ thực sự gặp vấn đề khi hết thời gian làm việc. Bài đăng này là về sự khác biệt và nên được sử dụng.
Damovisa

Câu trả lời:


670

Về cơ bản, họ cố gắng làm điều tương tự, nhưng setIntervalcách tiếp cận sẽ chính xác hơn setTimeoutcách tiếp cận, vì setTimeoutchờ 1000ms, chạy chức năng và sau đó đặt thời gian chờ khác. Vì vậy, thời gian chờ thực sự là hơn 1000ms một chút (hoặc nhiều hơn nữa nếu chức năng của bạn mất nhiều thời gian để thực thi).

Mặc dù người ta có thể nghĩ rằng setIntervalsẽ thực thi chính xác cứ sau 1000ms, nhưng điều quan trọng cần lưu ý là setIntervalcũng sẽ trì hoãn, vì JavaScript không phải là ngôn ngữ đa luồng, điều đó có nghĩa là - nếu có các phần khác của tập lệnh đang chạy - khoảng thời gian sẽ có chờ đợi điều đó kết thúc

Trong Fiddle này , bạn có thể thấy rõ rằng thời gian chờ sẽ bị tụt lại phía sau, trong khi khoảng thời gian gần như toàn bộ thời gian là gần 1 cuộc gọi / giây (mà kịch bản đang cố gắng thực hiện). Nếu bạn thay đổi biến tốc độ ở đầu thành một số nhỏ như 20 (có nghĩa là nó sẽ cố chạy 50 lần mỗi giây), khoảng thời gian đó sẽ không bao giờ đạt được trung bình 50 lần lặp mỗi giây.

Độ trễ hầu như không đáng kể, nhưng nếu bạn đang lập trình một cái gì đó thực sự chính xác, bạn nên sử dụng bộ hẹn giờ tự điều chỉnh (về cơ bản là bộ hẹn giờ dựa trên thời gian chờ liên tục tự điều chỉnh cho độ trễ mà nó tạo ra)


4
Andy đã có một đề nghị tương tự. Theo giả thuyết, điều này có nghĩa là nếu phương thức này mất hơn 1000ms để thực thi, bạn có thể có nhiều hơn một hoạt động đồng thời?
Damovisa

14
Về mặt lý thuyết, vâng. Trong thực tế, không vì Javascript không hỗ trợ đa luồng. Nếu mã của bạn mất nhiều hơn 1000ms, nó sẽ đóng băng trình duyệt.
Kamiel Wanrooij

61
Về mặt kỹ thuật, mã sẽ không thực thi chính xác cứ sau 1000ms, vì nó phụ thuộc vào độ phân giải của bộ định thời và liệu mã khác có đang thực thi hay không. Quan điểm của bạn vẫn đứng mặc dù.
Matthew Crumley

10
setInterval khác nhau theo hai cách, 1. setInterval không được đệ quy và setInterval lần đầu tiên sẽ gọi hàm của bạn sau thời gian đã nói trong khi setTimeout lần đầu tiên được gọi mà không có bất kỳ độ trễ nào và sau đó nó sẽ gọi lại sau thời gian đã nói. Sau khi thực hiện đầu tiên, chúng hoạt động gần như giống nhau.
Hafiz

5
Sự khác biệt là setTimeout không lặp lại. Nó cho phép bạn chạy một tập lệnh sau một thời gian đã đặt, nhưng chỉ một lần. Mặt khác, setInterval sẽ lặp lại tập lệnh đó, cho đến khi nó bị dừng với clearTimeout ().
Rời

648

Có sự khác biệt nào không?

Đúng. Thời gian chờ thực thi một khoảng thời gian nhất định sau khi setTimeout () được gọi; một Interval thực thi một khoảng thời gian nhất định sau khi khoảng thời gian trước đó được kích hoạt.

Bạn sẽ nhận thấy sự khác biệt nếu hàm doStuff () của bạn mất một lúc để thực thi. Ví dụ: nếu chúng tôi biểu thị một lệnh gọi đến setTimeout / setInterval với ., việc kích hoạt thời gian chờ / khoảng thời gian *và thực thi mã JavaScript với [-----], các mốc thời gian sẽ như sau:

Timeout:

.    *  .    *  .    *  .    *  .
     [--]    [--]    [--]    [--]

Interval:

.    *    *    *    *    *    *
     [--] [--] [--] [--] [--] [--]

Sự phức tạp tiếp theo là nếu một khoảng thời gian kích hoạt trong khi JavaScript đã bận làm việc gì đó (chẳng hạn như xử lý một khoảng trước đó). Trong trường hợp này, khoảng thời gian được ghi nhớ và xảy ra ngay khi trình xử lý trước kết thúc và trả lại quyền điều khiển cho trình duyệt. Vì vậy, ví dụ cho quy trình doStuff () đôi khi ngắn ([-]) và đôi khi dài ([-----]):

.    *    *        *        *    *
     [-]  [-----][-][-----][-][-]  [-]

• đại diện cho một khoảng thời gian bắn không thể thực thi mã của nó ngay lập tức và được thực hiện chờ xử lý thay thế.

Vì vậy, các khoảng thời gian cố gắng 'bắt kịp' để lấy lại lịch trình. Nhưng, họ không xếp hàng chồng lên nhau: chỉ có thể có một thực thi đang chờ xử lý cho mỗi khoảng. (Nếu tất cả họ xếp hàng, trình duyệt sẽ được để lại một danh sách thực thi ngày càng mở rộng!)

.    *            x            x
     [------][------][------][------]

x đại diện cho một khoảng thời gian bắn không thể thực hiện hoặc đang chờ xử lý, vì vậy thay vào đó đã bị loại bỏ.

Nếu chức năng doStuff () của bạn thường mất nhiều thời gian để thực thi hơn khoảng thời gian được đặt cho nó, trình duyệt sẽ ăn 100% CPU để phục vụ nó và có thể trở nên kém phản hồi hơn.

Bạn sử dụng cái nào và tại sao?

Chained-Timeout cung cấp một khoảng thời gian rảnh được đảm bảo cho trình duyệt; Interval cố gắng đảm bảo chức năng mà nó đang chạy thực thi càng gần với thời gian đã lên lịch của nó, với chi phí sẵn có cho trình duyệt UI.

Tôi sẽ xem xét một khoảng thời gian cho các hình ảnh động một lần mà tôi muốn trở nên mượt mà nhất có thể, trong khi thời gian bị xiềng xích sẽ lịch sự hơn cho các hình ảnh động đang diễn ra trong suốt thời gian trang được tải. Đối với việc sử dụng ít đòi hỏi hơn (chẳng hạn như một trình cập nhật tầm thường bắn mỗi 30 giây hoặc một cái gì đó), bạn có thể sử dụng một cách an toàn.

Về khả năng tương thích trình duyệt, setTimeout có trước setInterval, nhưng tất cả các trình duyệt bạn sẽ gặp hôm nay đều hỗ trợ cả hai. Straggler cuối cùng trong nhiều năm là IE Mobile trong WinMo <6.5, nhưng hy vọng rằng điều đó cũng đang ở phía sau chúng ta.


Nhưng liệu lý do lựa chọn giữa Interval và Timeout có đúng với các nền tảng không phải trình duyệt, như WebOS hay JIL không?
Vid L

2
Một cách hay để thực hiện thời gian chờ chuỗi trên các hàm ẩn danh: setTimeout (function () {setTimeout (argument.callee, 10)}, 10)
Justin Meyer

1
Về khả năng tương thích trình duyệt, mặc dù tất cả các trình duyệt cung cấp cả hai phương pháp, hiệu suất của chúng là khác nhau.
unigg

"Nhưng sau đó, rất có thể nó không hỗ trợ bất cứ thứ gì khác mà bạn đang sử dụng" rất đúng
Cơ bản

1
máy trống theo dự án crom sử dụng setTimeout ("calendar ()", 0); Vì vậy, trình duyệt sẽ không có ngăn xếp không cần thiết khi đó là tab nền cho chrome hoặc thiếu tài nguyên.
Elliot Yap

91

setInterval ()

setInterval()là một phương thức thực thi mã dựa trên khoảng thời gian có khả năng riêng để liên tục chạy một tập lệnh được chỉ định khi đạt được khoảng thời gian. Nó không nên được lồng vào chức năng gọi lại của nó bởi tác giả kịch bản để làm cho nó lặp, vì nó lặp theo mặc định . Nó sẽ tiếp tục bắn vào khoảng thời gian trừ khi bạn gọi clearInterval().

Nếu bạn muốn lặp mã cho hình động hoặc trên đồng hồ, hãy sử dụng setInterval().

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setInterval(doStuff, 5000);

setTimeout ()

setTimeout()là một phương thức thực thi mã dựa trên thời gian sẽ thực thi một tập lệnh chỉ một lần khi đạt được khoảng thời gian. Nó sẽ không lặp lại một lần nữa trừ khi bạn đưa nó vào vòng lặp script bằng cách lồng setTimeout()đối tượng bên trong hàm mà nó gọi để chạy. Nếu hướng đến vòng lặp, nó sẽ tiếp tục bắn theo khoảng thời gian trừ khi bạn gọi clearTimeout().

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setTimeout(doStuff, 5000);

Nếu bạn muốn một cái gì đó xảy ra một lần sau một khoảng thời gian xác định, sau đó sử dụng setTimeout(). Đó là bởi vì nó chỉ thực hiện một lần khi đạt đến khoảng thời gian được chỉ định.


6
Giải thích hay nhưng lưu ý rằng OP hiểu sự khác biệt cơ bản trong các ví dụ mà OP cung cấp. Cụ thể, lưu ý rằng trong setTimeout()ví dụ của OP , setTimeout()được gọi là đệ quy , trong khi setInterval()không.
DavidRR

44

SetInterval giúp hủy bỏ việc thực thi mã của bạn trong tương lai dễ dàng hơn. Nếu bạn sử dụng setTimeout, bạn phải theo dõi id bộ định thời trong trường hợp bạn muốn hủy nó sau này.

var timerId = null;
function myTimeoutFunction()
{
    doStuff();
    timerId = setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

// later on...
clearTimeout(timerId);

đấu với

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
var timerId = setInterval(myTimeoutFunction, 1000);

// later on...
clearInterval(timerId);

35
Tôi không thấy cách bạn không theo dõi id bộ đếm thời gian trong trường hợp setInterval. Nó vừa được rút ra khỏi chức năng.
Kekoa

1
Một tùy chọn khác setTimeoutthay vì lưu id thêm một ifcâu lệnh để chỉ đặt thời gian chờ tiếp theo khi một số điều kiện là đúng.
nnnnnn

7
Kekoa, điểm tốt. Nhưng bạn phải lưu id khoảng một lần. Id thời gian chờ có thể thay đổi theo mỗi lần gọi, vì vậy bạn phải theo dõi những thay đổi này. Mã muốn xóa thời gian chờ phải bằng cách nào đó có quyền truy cập vào giá trị hiện tại của biến này. Ngoài ra, không thể xóa thời gian chờ trong khi chạy doStuffvì id không hợp lệ. Tuy nhiên, không thực sự cần thiết để lưu id thời gian chờ. Thay vào đó bạn chỉ có thể ngừng gọi setTimeout.
Robert

4
Theo kinh nghiệm của tôi, hầu hết thời gian bạn muốn có thể hủy ngay cả khi sử dụng setTimeout. 99% thời gian bạn muốn hủy trước khi lần gọi tiếp theo xảy ra, không phải sau khi lần tiếp theo kết thúc.
Kamiel Wanrooij

23

Tôi thấy setTimeoutphương pháp dễ sử dụng hơn nếu bạn muốn hủy thời gian chờ:

function myTimeoutFunction() {
   doStuff();
   if (stillrunning) {
      setTimeout(myTimeoutFunction, 1000);
   }
}

myTimeoutFunction();

Ngoài ra, nếu có lỗi xảy ra trong hàm, nó sẽ chỉ dừng lại ở lỗi lần đầu tiên, thay vì lặp lại lỗi mỗi giây.


20

Sự khác biệt là trong mục đích của họ.

setInterval()
   -> executes a function, over and over again, at specified time intervals  

setTimeout()
   -> executes a function, once, after waiting a specified number of milliseconds

Nó đơn giản như vậy

Chi tiết chi tiết hơn tại đây http://javascript.info/tutorial/settimeout-setinterval


7
Giải thích ngắn gọn súc tích nhưng lưu ý rằng OP hiểu sự khác biệt cơ bản trong các ví dụ mà OP cung cấp. Cụ thể, lưu ý rằng trong setTimeout()ví dụ của OP , setTimeout()được gọi là đệ quy , trong khi setInterval()không.
DavidRR

14

Khi bạn chạy một số chức năng bên trong setInterval, hoạt động nhiều thời gian hơn thời gian chờ-> trình duyệt sẽ bị kẹt.

- Ví dụ: doStuff () mất 1500 giây. để được thực thi và bạn làm: setInterval (doStuff, 1000);
1) Trình duyệt chạy doStuff () mất 1,5 giây. để được thực thi;
2) Sau ~ 1 giây, nó cố chạy lại doStuff () . Nhưng doStuff () trước đó vẫn được thực thi-> vì vậy trình duyệt thêm lần chạy này vào hàng đợi (để chạy sau lần đầu tiên được thực hiện).
3,4, ..) Việc thêm vào hàng đợi thực hiện cho các lần lặp tiếp theo, nhưng doStuff () từ trước đó vẫn đang được tiến hành ...
Kết quả là trình duyệt bị kẹt.

Để ngăn chặn hành vi này, cách tốt nhất là chạy setTimeout bên trong setTimeout để mô phỏng setInterval .
Để sửa thời gian chờ giữa các cuộc gọi setTimeout, bạn có thể sử dụng thay thế tự sửa cho kỹ thuật setInterval của JavaScript .


1
Tôi không biết tại sao không ai tìm thấy bất kỳ giá trị nào trong câu trả lời này!
Randika Vishman

9

Tôi sử dụng setTimeout.

Rõ ràng sự khác biệt là setTimeout gọi phương thức một lần, setInterval gọi nó lặp lại.

Đây là một bài viết hay giải thích sự khác biệt: Hướng dẫn: Bộ định thời JavaScript với setTimeout và setInterval


2
Đúng, tôi có sự khác biệt đó, nhưng hai đoạn mã tôi đã cung cấp sau đó sẽ làm điều tương tự ...
Damovisa

Ummm vâng ... tôi đã có thể nghĩ ... nhưng theo dcistic và số phiếu bầu của anh ấy không hoàn toàn là những gì xảy ra.
Bravax

9

Mã của bạn sẽ có các nội dung thực thi khác nhau và trong một số dự án, chẳng hạn như các trò chơi trực tuyến, điều đó không được chấp nhận. Trước tiên, bạn nên làm gì, để làm cho mã của bạn hoạt động với cùng một nội dung, bạn nên thay đổi "myTimeoutFunction" thành điều này:

function myTimeoutFunction()
{
    setTimeout(myTimeoutFunction, 1000);
    doStuff();
}
myTimeoutFunction()

Sau thay đổi này, nó sẽ bằng

function myTimeoutFunction()
{
    doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Nhưng, bạn vẫn sẽ không có kết quả ổn định, bởi vì JS là luồng đơn. Hiện tại, nếu luồng JS sẽ bận rộn với một cái gì đó, nó sẽ không thể thực thi chức năng gọi lại của bạn và việc thực thi sẽ bị hoãn lại trong 2-3 giây. Bạn có 60 lần thực hiện mỗi giây không và mỗi lần bạn có độ trễ ngẫu nhiên 1-3 giây, điều đó sẽ hoàn toàn không được chấp nhận (sau một phút sẽ chậm khoảng 7200 ms) và tôi có thể khuyên bạn nên sử dụng một cái gì đó như thế này:

    function Timer(clb, timeout) {
        this.clb = clb;
        this.timeout = timeout;
        this.stopTimeout = null;
        this.precision = -1;
    }

    Timer.prototype.start = function() {
        var me = this;
        var now = new Date();
        if(me.precision === -1) {
            me.precision = now.getTime();
        }
        me.stopTimeout = setTimeout(function(){
            me.start()
        }, me.precision - now.getTime() + me.timeout);
        me.precision += me.timeout;
        me.clb();
    };

    Timer.prototype.stop = function() {
        clearTimeout(this.stopTimeout);
        this.precision = -1;
    };

    function myTimeoutFunction()
    {
        doStuff();
    }

    var timer = new Timer(myTimeoutFunction, 1000);
    timer.start();

Mã này sẽ đảm bảo thời gian thực hiện ổn định. Ngay cả luồng sẽ bận và mã của bạn sẽ được thực thi sau 1005 giây, lần sau nó sẽ có thời gian chờ là 995 ms và kết quả sẽ ổn định.


7

Tôi đã thực hiện thử nghiệm đơn giản setInterval(func, milisec)vì tôi tò mò chuyện gì sẽ xảy ra khi mức tiêu thụ thời gian của hàm lớn hơn thời lượng giữa các lần.

setIntervalnói chung sẽ lên lịch cho lần lặp tiếp theo ngay sau khi bắt đầu lần lặp trước, trừ khi chức năng vẫn đang tiếp diễn . Nếu vậy, setIntervalsẽ chờ, cho đến khi chức năng kết thúc. Ngay khi nó xảy ra, chức năng sẽ ngay lập tức được kích hoạt lại - không phải chờ đợi lần lặp tiếp theo theo lịch trình (vì nó sẽ ở trong điều kiện không có thời gian vượt quá chức năng). Cũng không có tình huống với các bước lặp song song đang chạy.

Tôi đã thử nghiệm điều này trên Chrome v23. Tôi hy vọng nó là triển khai xác định trên tất cả các trình duyệt hiện đại.

window.setInterval(function(start) {
    console.log('fired: ' + (new Date().getTime() - start));
    wait();
  }, 1000, new Date().getTime());

Bảng điều khiển đầu ra:

fired: 1000    + ~2500 ajax call -.
fired: 3522    <------------------'
fired: 6032
fired: 8540
fired: 11048

Các waitchức năng chỉ là một sợi chặn helper - đồng bộ ajax gọi mà mất chính xác 2500 mili giây chế biến ở phía máy chủ:

function wait() {
    $.ajax({
        url: "...",
        async: false
    });
}

1
"không có tình huống với các bước lặp song song đang chạy" - vâng, điều này là không thể. JavaScript phía máy khách có một mô hình thực thi luồng đơn để không có gì xảy ra (trình xử lý sự kiện, v.v.) xảy ra cùng một lúc. Đó là lý do tại sao không có gì xảy ra (và trình duyệt không phản hồi) trong cuộc gọi ajax đồng bộ.
MrWhite

Trình duyệt có phản hồi trong vài ms giữa các "khoảng" không? Một sự kiện khác sẽ cháy nếu nó đang chờ xử lý? (Cảm ơn bạn đã kiểm tra btw +1)
MrWhite

1
Theo MDN, nó được coi là " sử dụng nguy hiểm " nếu chức năng có thể thực thi lâu hơn khoảng thời gian. Một setTimoutcuộc gọi đệ quy được ưa thích.
MrWhite

6

Để xem xét nó một chút khác biệt: setInterval đảm bảo rằng mã được chạy ở mỗi khoảng thời gian nhất định (ví dụ 1000ms hoặc bao nhiêu bạn chỉ định) trong khi setTimeout đặt thời gian mà nó 'đợi cho đến khi nó chạy mã. Và vì phải mất thêm mili giây để chạy mã, nên nó tăng thêm 1000ms và do đó, setTimeout chạy lại ở thời điểm không chính xác (hơn 1000ms).

Ví dụ: bộ tính giờ / đếm ngược không được thực hiện với setTimeout, chúng được thực hiện với setInterval, để đảm bảo nó không bị trì hoãn và mã chạy ở khoảng thời gian chính xác.


5

Cả setInterval và setTimeout đều trả về id bộ đếm thời gian mà bạn có thể sử dụng để hủy thực thi, nghĩa là trước khi hết thời gian chờ được kích hoạt. Để hủy, bạn gọi ClearInterval hoặc clearTimeout như thế này:

var timeoutId = setTimeout(someFunction, 1000);
clearTimeout(timeoutId);
var intervalId = setInterval(someFunction, 1000),
clearInterval(intervalId);

Ngoài ra, thời gian chờ sẽ tự động bị hủy khi bạn rời khỏi trang hoặc đóng cửa sổ trình duyệt.


4

Bạn có thể tự xác thực câu trả lời bobince khi bạn chạy javascript sau hoặc kiểm tra JSFiddle này

<div id="timeout"></div>
<div id="interval"></div>

var timeout = 0;
var interval = 0;

function doTimeout(){
    $('#timeout').html(timeout);
    timeout++;
    setTimeout(doTimeout, 1);
}

function doInterval(){
    $('#interval').html(interval);
    interval++;
}

$(function(){
    doTimeout();
    doInterval();
    setInterval(doInterval, 1);
});

3

Chà, setTimeout tốt hơn trong một tình huống, như tôi vừa tìm hiểu. Tôi luôn sử dụng setInterval, cái mà tôi còn lại để chạy trong nền hơn nửa giờ. Khi tôi chuyển trở lại tab đó, trình chiếu (trên đó mã được sử dụng) đã thay đổi rất nhanh, thay vì cứ sau 5 giây mà nó nên có. Thực tế nó lại xảy ra một lần nữa khi tôi kiểm tra nó nhiều hơn và liệu đó có phải là lỗi của trình duyệt hay không, vì với setTimeout, tình huống đó là hoàn toàn không thể.


Âm thanh như thể bạn đã gọi setInterval bên trong chức năng mà bạn đang gọi. Đó là cách bạn lặp với setTimeout, nhưng với setInterval bạn thực sự tạo ra một vòng lặp hoàn toàn mới mỗi lần nó được gọi.
Stephen R


2

Chỉ cần thêm vào những gì đã được nói nhưng phiên bản mã setTimeout cũng sẽ đạt đến Maximum call stack sizecái sẽ ngăn nó hoạt động. Vì không có trường hợp cơ sở nào cho chức năng đệ quy dừng lại nên bạn không thể chạy nó mãi mãi.


Tôi không nghĩ điều này là đúng. Xem tại đây stackoverflow.com/questions/8058612/ Mạnh
Kevin Wheeler

-1

Sự khác biệt chung nhất giữa và setInterval (biểu thức, thời lượng) là

setTimeout () sẽ chỉ thực hiện biểu thức ONCE và sau khoảng thời gian được chỉ định đó.

Tuy nhiên,

setInterval () sẽ thực thi biểu thức trong INFINITE-LOOP sau mỗi khoảng thời gian được chỉ định.


-5

Tôi nghĩ SetIntervalSetTimeoutkhác biệt. SetIntervalthực thi khối theo thời gian được đặt trong khi,SetTimeout thực thi khối mã một lần.

Hãy thử các bộ mã này sau giây đếm ngược thời gian chờ:

setInterval(function(e){
    alert('Ugbana Kelvin');
}, 2000);

và sau đó thử

setTimeout(function(e){
    alert('Ugbana Kelvin');
}, 2000);

Bạn có thể thấy sự khác biệt cho chính mình.


Điều này không trả lời câu hỏi. giải quyết () có thể được sử dụng đệ quy để cho kết quả tương tự như setinterval. Câu hỏi là về hiệu suất của hai tùy chọn này.
Tomas Gonzalez
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.