เบราว์เซอร์หยุดชั่วคราว / เปลี่ยน Javascript อย่างไรเมื่อแท็บหรือหน้าต่างไม่ทำงาน


168

ความเป็นมา: ฉันกำลังทำการทดสอบส่วนต่อประสานผู้ใช้ที่จำเป็นต้องตรวจสอบว่าผู้คนให้ความสนใจหรือไม่ แต่คำถามนี้ไม่ได้เกี่ยวกับการมองเห็นหน้า API

โดยเฉพาะฉันต้องการทราบว่าโค้ด Javascript ของฉันจะได้รับผลกระทบอย่างไรหากแท็บปัจจุบันไม่ทำงานหรือหน้าต่างเบราว์เซอร์ไม่ทำงานในเบราว์เซอร์ที่แตกต่างกัน ฉันขุดต่อไปจนถึง:

ฉันมีคำถามต่อไปนี้:

  • นอกเหนือจากเบราว์เซอร์มือถือเดสก์ท็อปเบราว์เซอร์จะหยุดการประมวลผล JS ชั่วคราวเมื่อแท็บไม่ทำงานหรือไม่ เบราว์เซอร์ใดและเมื่อใด
  • เบราว์เซอร์ใดลดการsetIntervalซ้ำ มันลดลงถึงขีด จำกัด หรือโดยเปอร์เซ็นต์? ตัวอย่างเช่นหากฉันมีการทำซ้ำ 10 มิลลิเซคอนกับการทำซ้ำ 5,000 มิลลิวินาทีแต่ละครั้งจะได้รับผลกระทบอย่างไร
  • การเปลี่ยนแปลงเหล่านี้เกิดขึ้นหรือไม่หากหน้าต่างนั้นไม่ได้โฟกัสเมื่อเทียบกับแท็บ (ฉันคิดว่ามันจะยากต่อการตรวจสอบเนื่องจากต้องใช้ OS API)
  • มีเอฟเฟกต์อื่น ๆ ที่จะไม่สังเกตเห็นในแท็บที่ใช้งานอยู่หรือไม่? พวกเขาสามารถทำสิ่งต่าง ๆ ที่จะดำเนินการอย่างถูกต้อง (เช่นการทดสอบจัสมินดังกล่าวข้างต้น)?

หากหยุดชั่วคราวไซต์เช่น Facebook จะไม่ได้รับข้อความแชทบนแท็บพื้นหลัง
Joseph

1
ใช่ไม่มีการหยุดชั่วคราว แต่ฉันจำได้ว่าอ่านsetInterval/ setTimeoutครั้งภายใต้ 1000ms มีการเปลี่ยนแปลงเพื่อ 1000ms เมื่อแท็บ / หน้าต่างจะเบลอ
เอียน

19
@ProfPickle เว็บมาสเตอร์? จริงๆ? นี่คือคำถามการเขียนโปรแกรม JS
Andrew Mao

1
@lan setInterval/ setTimeoutครั้งต่ำกว่า1,000 มิลลิ วินาทีจะเปลี่ยนเป็น 1,000 มิลลิวินาทีเมื่อแท็บ / หน้าต่างเบลอ ไม่ชัดเจนสิ่งที่คุณพยายามสื่อ
Amol M Kulkarni

4
+1 คำถามที่ยอดเยี่ยม มันเป็นการดีที่จะเห็นการเปรียบเทียบพฤติกรรมของเบราว์เซอร์แบบเคียงข้างกันเพราะฉันเชื่อว่าพฤติกรรมการจับเมื่อแท็บไม่ทำงานไม่ได้เป็นส่วนหนึ่งของมาตรฐานใด ๆ
UpTheCreek

คำตอบ:


190

ทดสอบหนึ่ง

ฉันได้เขียนการทดสอบเพื่อจุดประสงค์นี้โดยเฉพาะ: การ
กระจายอัตราเฟรม: setInterval เทียบกับ requestAnimationFrame

หมายเหตุ: การทดสอบนี้ค่อนข้างเข้มข้นของ CPU requestAnimationFrameไม่รองรับ IE 9- และ Opera 12-

การทดสอบจะบันทึกเวลาตามจริงสำหรับ a setIntervalและrequestAnimationFrameทำงานในเบราว์เซอร์ที่ต่างกันและให้ผลลัพธ์ในรูปแบบของการแจกแจง คุณสามารถเปลี่ยนจำนวนมิลลิวินาทีsetIntervalเพื่อดูว่ามันทำงานอย่างไรภายใต้การตั้งค่าที่แตกต่างกัน setTimeoutทำงานคล้ายsetIntervalกับที่เกี่ยวกับความล่าช้า requestAnimationFrameโดยทั่วไปค่าเริ่มต้นที่ 60fps ขึ้นอยู่กับเบราว์เซอร์ หากต้องการดูว่าเกิดอะไรขึ้นเมื่อคุณสลับไปที่แท็บอื่นหรือมีหน้าต่างที่ไม่ใช้งานอยู่เพียงแค่เปิดหน้านั้นสลับไปที่แท็บอื่นแล้วรอสักครู่ มันจะยังคงบันทึกเวลาตามจริงสำหรับฟังก์ชั่นเหล่านี้ในแท็บที่ไม่ได้ใช้งาน

ทดสอบสอง

วิธีการทดสอบมันก็คือการเข้าสู่ระบบการประทับเวลาซ้ำ ๆ ด้วยsetIntervalและrequestAnimationFrameและดูในคอนโซลแฝด คุณสามารถดูว่ามีการอัปเดตบ่อยเพียงใด (หรือเคยอัปเดต) เมื่อคุณทำให้แท็บหรือหน้าต่างไม่ทำงาน

ผล

Chrome
Chrome จำกัด ช่วงเวลาต่ำสุดsetIntervalที่ประมาณ 1000ms เมื่อแท็บไม่ทำงาน หากช่วงเวลานั้นสูงกว่า 1,000 มิลลิวินาทีมันจะทำงานตามช่วงเวลาที่ระบุ ไม่สำคัญว่าหน้าต่างจะไม่โฟกัสหรือไม่ช่วงเวลาจะถูก จำกัด เฉพาะเมื่อคุณสลับไปที่แท็บอื่น requestAnimationFrameหยุดชั่วคราวเมื่อแท็บไม่ทำงาน

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

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

Firefox
คล้ายกับ Chrome Firefox จะ จำกัด ช่วงเวลาขั้นต่ำไว้setIntervalที่ประมาณ 1,000 มิลลิวินาทีเมื่อแท็บ (ไม่ใช่หน้าต่าง) ไม่ทำงาน อย่างไรก็ตามrequestAnimationFrameจะทำงานช้าลงอย่างมากเมื่อแท็บไม่ทำงานโดยแต่ละเฟรมใช้เวลา 1 วินาที, 2 วินาที, 4s, 8 วินาทีเป็นต้น

// 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/releases/mozilla-release/file/0bf1cadfb004/dom/base/nsGlobalWindow.cpp#l296

Internet Explorer
IE ไม่ได้ จำกัด การหน่วงเวลาsetIntervalเมื่อแท็บไม่ทำงาน แต่จะหยุดrequestAnimationFrameในแท็บที่ไม่ทำงาน มันไม่สำคัญว่าหน้าต่างจะไม่โฟกัสหรือไม่

Edge
เริ่มต้นจาก Edge 14, setIntervalต่อยอดที่ 1000ms ในแท็บที่ไม่ได้ใช้งาน requestAnimationFrameถูกหยุดชั่วคราวในแท็บที่ไม่ใช้งาน

Safari
เช่นเดียวกับ Chrome และ Safari จะsetIntervalอยู่ที่ 1000 มิลลิวินาทีเมื่อแท็บไม่ทำงาน requestAnimationFrameหยุดชั่วคราวเช่นกัน

โอเปร่า
นับตั้งแต่มีการนำ Webkit engine มาใช้แสดงว่าพฤติกรรมเช่นเดียวกับ Chrome setIntervalถูก จำกัด ที่ 1,000ms และrequestAnimationFrameหยุดชั่วคราวเมื่อแท็บไม่ทำงาน

สรุป

ทำซ้ำช่วงเวลาสำหรับแท็บที่ไม่ได้ใช้งาน:

           setInterval      requestAnimationFrame 
Chrome
9- ไม่ได้รับผลกระทบไม่ได้รับการสนับสนุน
10 ไม่ได้รับผลกระทบหยุดชั่วคราว
11+> = 1000ms หยุดชั่วคราว

Firefox
3- ไม่ได้รับผลกระทบไม่ได้รับการสนับสนุน
4 ไม่ได้รับผลกระทบ 1 วินาที
5+> = 1000ms 2 n s (n = จำนวนเฟรมตั้งแต่ไม่มีกิจกรรม)

IE
9- ไม่ได้รับผลกระทบไม่ได้รับการสนับสนุน
10+ ไม่ได้รับผลกระทบหยุดชั่วคราว

ขอบ
13- ไม่ได้รับผลกระทบหยุดชั่วคราว
14+> = 1000ms หยุดชั่วคราว

การแข่งรถวิบาก
5- ไม่ได้รับผลกระทบไม่ได้รับการสนับสนุน
6 ไม่ได้รับผลกระทบหยุดชั่วคราว
7+> = 1000ms หยุดชั่วคราว

อุปรากร
12- ไม่ได้รับผลกระทบไม่ได้รับการสนับสนุน
15+> = 1000ms หยุดชั่วคราว

คำตอบที่ดี ความแตกต่างใด ๆ ที่เป็นที่รู้จักกันเป็นไปได้อื่น ๆ สำหรับฟังก์ชั่นอื่น ๆ กว่าsetIntervalและrequestAnimationFrame?
Andrew Mao

1
@AndrewMao ไม่ว่าฉันรู้ ฉันมาข้ามปัญหานี้เมื่อผมทำงานในห้องสมุดในการตรวจสอบความน่าเชื่อถือถ้า JS reenabled ด้วยและsetInterval requestAnimationFrameสิ่งที่ฉันรู้คือsetTimeoutพฤติกรรมคล้ายกันsetIntervalโดยที่ทั้งคู่มีช่วงเวลาพื้นหลังขั้นต่ำเท่ากันใน Firefox และ Chrome และไม่มีข้อ จำกัด ที่ชัดเจนในเบราว์เซอร์อื่น
Antony

2
ค่าต่ำสุดของ Firefox setInterval สามารถเปลี่ยนได้โดยการเปิด url about:configในเบราว์เซอร์และเปลี่ยนdom.min_background_timeout_valueค่าเป็นอย่างอื่นมากกว่า 1,000
Jonas Berlin

ฉันสามารถใช้มันเพื่อโหลดหน้าซ้ำทุก ๆ 5 วินาทีเมื่อเบราว์เซอร์ย่อเล็กสุดได้หรือไม่นี่คือคำถามของฉัน
shaijut

1
โปรดทราบว่าโครเมี่ยมไม่ได้หยุด / ลดอัตราที่requestAnimationFrameเรียกว่าถ้าผู้ใช้เพียงแค่เปลี่ยนแอปพลิเคชัน (Alt + Tab out จาก Chrome) ตราบใดที่แท็บนั้นยังทำงานอยู่ใน Chrome "อัตราเฟรม" คงที่ไม่มากก็น้อย
Marc

11

สิ่งที่ฉันสังเกตเห็น: ในแท็บที่ไม่ใช้งานในChromeทั้งหมดของคุณsetTimeout(ต้องเหมือนกันสำหรับsetInterval ) รอน้อยกว่า1000msมีการปัดเศษเพื่อ1000ms ฉันคิดว่าการหมดเวลานานขึ้นจะไม่ถูกแก้ไข

ดูเหมือนจะเป็นพฤติกรรมตั้งแต่Chrome 11และFirefox 5.0 : https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

นอกจากนี้ฉันไม่คิดว่ามันจะทำงานแบบนี้เมื่อหน้าต่างทั้งหมดไม่ทำงาน (แต่ดูเหมือนง่ายมากที่จะตรวจสอบ)


1
jQuery focusและblurกิจกรรมดูเหมือนจะตรวจจับทั้งแท็บและสวิตช์หน้าต่างดังนั้นจึงอาจทำงานได้ทั้งสองวิธี แต่ฉันสงสัยว่าหน้าต่างตรวจพบได้จริงหรือไม่
Andrew Mao

2
จริงๆแล้วมันไม่มีการเชื่อมต่อกับjQueryหรือJavascriptเนื่องจากเป็นการใช้งานเบราว์เซอร์ภายใน

คุณสามารถยืนยันได้ตอนนี้ในปลายปี 2016?
vsync

0

คำตอบใหม่เพื่อเสริมเหล่านี้: บน Chrome 78.0.3904.108 ผมสังเกตเห็นทั้งหมดของหมดเวลาเหล่านี้ (ไม่เพียง แต่ผู้ 1000ms ด้านล่าง) การอีกนานกว่าที่คาดไว้เมื่อตอนที่ผมย้ายไปที่แท็บที่แตกต่างกันแล้วกลับมา พฤติกรรมที่ฉันเห็นมีการอธิบายอย่างถูกต้องมากขึ้นว่า"การหมดเวลาทั้งหมดในแท็บที่ไม่ได้ใช้งานอาจล่าช้าโดยจำนวนเงินเพิ่มเติมบางส่วนได้สูงสุด 1,000 มิลลิวินาที" :

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>

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