Làm cách nào để trình duyệt tạm dừng / thay đổi Javascript khi tab hoặc cửa sổ không hoạt động?


168

Bối cảnh: Tôi đang thực hiện một số kiểm tra giao diện người dùng cần phát hiện xem mọi người có chú ý hay không. Nhưng, câu hỏi này không phải là về API hiển thị trang .

Cụ thể, tôi muốn biết mã Javascript của mình sẽ bị ảnh hưởng như thế nào nếu tab hiện tại không hoạt động hoặc cửa sổ trình duyệt không hoạt động, trong các trình duyệt khác nhau. Tôi đã đào lên những điều sau đây cho đến nay:

Tôi có những câu hỏi sau:

  • Khác với trình duyệt trên thiết bị di động, trình duyệt trên máy tính để bàn có bao giờ tạm dừng thực thi JS khi một tab không hoạt động không? Khi nào và trình duyệt nào?
  • Những trình duyệt nào làm giảm sự setIntervallặp lại? Có phải nó chỉ giảm đến một giới hạn hoặc theo tỷ lệ phần trăm? Ví dụ: nếu tôi lặp lại 10ms so với lặp lại 5000ms, mỗi lần sẽ bị ảnh hưởng như thế nào?
  • Những thay đổi này có xảy ra nếu cửa sổ nằm ngoài tiêu cự, trái ngược với chỉ tab không? (Tôi tưởng tượng sẽ khó phát hiện hơn vì nó yêu cầu API hệ điều hành.)
  • Có bất kỳ hiệu ứng nào khác sẽ không được quan sát trong một tab hoạt động không? Họ có thể làm mọi thứ rối tung lên nếu không thực hiện chính xác (ví dụ như các bài kiểm tra Jasmine đã nói ở trên)?

Nếu chúng bị tạm dừng, các trang web như Facebook sẽ không nhận được bất kỳ tin nhắn trò chuyện nào trên các tab nền.
Joseph

1
Vâng không có tạm dừng, nhưng tôi nhớ rằng đọc setInterval/ setTimeoutlần dưới 1000ms được thay đổi thành 1000ms khi tab / cửa sổ bị mờ
Ian

19
@ProfPickle Quản trị trang web? Có thật không? Đây là một câu hỏi lập trình JS.
Andrew Mao

1
@lan setInterval/ setTimeoutlần dưới 1000ms được thay đổi thành 1000ms khi tab / cửa sổ bị mờ. Không rõ những gì bạn đã cố gắng truyền đạt
Amol M Kulkarni

4
+1 Câu hỏi tuyệt vời. Sẽ rất tốt khi thấy sự so sánh song song các hành vi của trình duyệt, vì tôi tin rằng hành vi kẹp khi các tab không hoạt động không phải là một phần của bất kỳ tiêu chuẩn nào.
UpTheCux

Câu trả lời:


190

Kiểm tra một

Tôi đã viết một bài kiểm tra cụ thể cho mục đích này:
Phân phối tỷ lệ khung hình: setInterval vs requestAnimationFrame

Lưu ý: Bài kiểm tra này khá tốn CPU. requestAnimationFramekhông được IE 9- và Opera 12- hỗ trợ.

Kiểm tra ghi lại thời gian thực tế cần thiết để chạy setIntervalrequestAnimationFramechạy trong các trình duyệt khác nhau và cung cấp cho bạn kết quả dưới dạng phân phối. Bạn có thể thay đổi số mili giây setIntervalđể xem cách nó chạy trong các cài đặt khác nhau. setTimeouthoạt động tương tự như setIntervalđối với sự chậm trễ. requestAnimationFramethường mặc định là 60fps tùy theo trình duyệt. Để xem điều gì xảy ra khi bạn chuyển sang một tab khác hoặc có một cửa sổ không hoạt động, chỉ cần mở trang, chuyển sang một tab khác và chờ một lúc. Nó sẽ tiếp tục ghi lại thời gian thực tế cần thiết cho các chức năng này trong một tab không hoạt động.

Kiểm tra hai

Một cách khác để kiểm tra nó là để ghi lại các dấu thời gian liên tục với setIntervalrequestAnimationFramevà xem nó trong một giao diện điều khiển tách ra. Bạn có thể thấy tần suất cập nhật (hoặc nếu nó được cập nhật) khi bạn làm cho tab hoặc cửa sổ không hoạt động.

Các kết quả

Chrome
Chrome giới hạn khoảng thời gian tối thiểu khoảng setInterval1000ms khi tab không hoạt động. Nếu khoảng thời gian cao hơn 1000ms, nó sẽ chạy ở khoảng thời gian được chỉ định. Không có vấn đề gì nếu cửa sổ nằm ngoài tiêu cự, khoảng thời gian chỉ bị giới hạn khi bạn chuyển sang một tab khác. requestAnimationFramebị tạm dừng khi tab không hoạt động.

// Provides control over the minimum timer interval for background tabs.
const double kBackgroundTabTimerInterval = 1.0;

https://codereview.chromium.org/6546021/patch/1001/2001

Firefox
Tương tự như Chrome, Firefox giới hạn khoảng thời gian tối thiểu khoảng setInterval1000ms khi tab (không phải cửa sổ) không hoạt động. Tuy nhiên, requestAnimationFramechạy chậm hơn theo cấp số nhân khi tab không hoạt động, với mỗi khung hình mất 1 giây, 2 giây, 4 giây, 8 giây, v.v.

// The default shortest interval/timeout we permit
#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms

https://hg.mozilla.org/release/mozilla-release/file/0bf1cadfb004/dom/base/nsGlobalWindow.cpp#l296

Internet Explorer
IE không giới hạn độ trễ setIntervalkhi tab không hoạt động, nhưng nó tạm dừng requestAnimationFrametrong các tab không hoạt động. Không quan trọng cửa sổ có nằm ngoài tiêu cự hay không.

Edge
Bắt đầu từ Edge 14, setIntervalđược giới hạn ở 1000ms trong các tab không hoạt động. requestAnimationFrameluôn bị tạm dừng trong các tab không hoạt động.

Safari
Cũng giống như Chrome, Safari giới hạn setIntervalở 1000ms khi tab không hoạt động. requestAnimationFramecũng bị tạm dừng.

Opera
Kể từ khi áp dụng công cụ Webkit, Opera thể hiện hành vi tương tự như Chrome. setIntervalđược giới hạn ở 1000ms và requestAnimationFramebị tạm dừng khi tab không hoạt động.

Tóm lược

Lặp lại các khoảng thời gian cho các tab không hoạt động:

           setInterval      requestAnimationFrame 
Chrome
9- không bị ảnh hưởng không được hỗ trợ
10 không bị ảnh hưởng tạm dừng
11+> = 1000ms đã tạm dừng

Firefox
3- không bị ảnh hưởng không được hỗ trợ
4 không bị ảnh hưởng
5+> = 1000ms 2 n s (n = số khung hình kể từ khi không hoạt động)

I E
9- không bị ảnh hưởng không được hỗ trợ
10+ không bị ảnh hưởng tạm dừng

Cạnh
13- không bị ảnh hưởng tạm dừng
14+> = 1000ms đã tạm dừng

Safari
5- không bị ảnh hưởng không được hỗ trợ
6 không bị ảnh hưởng tạm dừng
7+> = 1000ms bị tạm dừng

Opera
12- không bị ảnh hưởng không được hỗ trợ
15+> = 1000ms bị tạm dừng

Câu trả lời chính xác. Bất kỳ sự khác biệt có thể biết khác cho các chức năng khác hơn setIntervalrequestAnimationFrame?
Andrew Mao

1
@AndrewMao Không phải tôi biết. Tôi đã gặp vấn đề này khi tôi đang làm việc trên một thư viện để phát hiện một cách đáng tin cậy nếu JS có được bật lại setIntervalrequestAnimationFrame. Điều tôi biết là setTimeouthoạt động tương tự setInterval, ở chỗ cả hai đều có khoảng thời gian nền tối thiểu giống nhau trong Firefox và Chrome và không có giới hạn rõ ràng trong các trình duyệt khác.
Antony

2
Giá trị tối thiểu của Firefox setInterval rõ ràng có thể được thay đổi bằng cách mở url about:configtrong trình duyệt và thay đổi dom.min_background_timeout_valuegiá trị thành một thứ khác hơn 1000.
Jonas Berlin

Tôi có thể sử dụng điều này để tải lại trang cứ sau 5 giây khi trình duyệt được thu nhỏ không?, đây là câu hỏi của tôi.
shaijut

1
Lưu ý rằng chrome không tạm dừng / giảm tốc độ requestAnimationFrameđược gọi nếu người dùng chỉ cần chuyển ứng dụng (Alt + Tab ra khỏi Chrome). Miễn là tab hoạt động trong Chrome, "tốc độ khung hình" ít nhiều không đổi.
Marc

11

Những gì tôi đã quan sát: trên các tab không hoạt động trong Chrome , tất cả setTimeout(phải giống nhau setInterval) của bạn chờ dưới 1000ms được làm tròn thành 1000ms . Tôi nghĩ thời gian chờ lâu hơn không được sửa đổi.

Có vẻ là hành vi kể từ Chrome 11Firefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

Hơn nữa, tôi không nghĩ rằng nó hoạt động theo cách này khi toàn bộ cửa sổ không hoạt động (nhưng có vẻ như khá dễ điều tra).


1
jQuery focusblurcác sự kiện dường như phát hiện cả chuyển đổi tab và cửa sổ, do đó, nó có thể hoạt động theo cả hai cách. Nhưng tôi tự hỏi làm thế nào cửa sổ phát hiện nếu nó thực sự nhìn thấy hay không.
Andrew Mao

2
Trên thực tế, nó không có kết nối với jQuery hay Javascript vì nó là trình duyệt nội bộ.

Bạn có thể xác nhận điều này bây giờ vào cuối năm 2016?
vsync

0

Một câu trả lời mới hơn để bổ sung cho những điều này: trên chrome 78.0.3904.108 Tôi nhận thấy tất cả các thời gian chờ này (không chỉ những khoảng dưới 1000ms) mất nhiều thời gian hơn dự kiến ​​khi tôi chuyển sang một tab khác, rồi quay lại. Hành vi tôi đang thấy được mô tả chính xác hơn là "Tất cả thời gian chờ trên các tab không hoạt động có thể bị trì hoãn bởi một số lượng bổ sung, tối đa là 1000ms." :

let timeouts = [ 500, 1000, 2000, 3000, 10000 ];

let minExcess = document.getElementsByClassName('minExcess')[0];

timeouts.forEach(ms => {
  let elem = document.getElementsByClassName(`t${ms}`)[0];
  let cnt = 0;
  
  let lastMs = +new Date();
  let f = () => {
    let curMs = +new Date();
    let disp = document.createElement('p');
    let net = curMs - lastMs;
    lastMs = curMs;
        
    setTimeout(f, ms);
    if (minExcess.value && (net - ms) < parseInt(minExcess.value)) return;
    
    disp.innerText = `${net},`;
    elem.appendChild(disp);
    if (++cnt > 10) elem.firstElementChild.remove();
    
  };
  setTimeout(f, ms);
  
});
body { font-size: 80%; }
div {
  max-height: 80px;
  overflow-x: auto;
  background-color: rgba(0, 0, 0, 0.1);
  margin-bottom: 2px;
  white-space: nowrap;
}
p { margin: 0; }
div > p {
  margin: 0;
  display: inline-block;
  vertical-align: top;
  margin-right: 2px;
}
input { margin: 0 0 10px 0; }
.t500:before { display: block; content: '500ms'; font-weight: bold; }
.t1000:before { display: block; content: '1000ms'; font-weight: bold; }
.t2000:before { display: block; content: '2000ms'; font-weight: bold; }
.t3000:before { display: block; content: '3000ms'; font-weight: bold; }
.t10000:before { display: block; content: '10000ms'; font-weight: bold; }
<p>Ignore any values delayed by less than this amount:</p>
<input type="text" class="minExcess" value="200" pattern="^[0-9]*$"/>
<div class="timeout t500"></div>
<div class="timeout t1000"></div>
<div class="timeout t2000"></div>
<div class="timeout t3000"></div>
<div class="timeout t10000"></div>

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.