时间切片
1. 下次绘制交互 (INP)
下次绘制交互 (INP) 是一项新的指标,浏览器计划于 2024 年 3 月将其取代取代首次输入延迟 (FID) ,成为最新的 Web Core Vitals
(Web 核心性能指标)。
2. 时间切片-scheduler.yield
背景:用户任务完成自动释放控制权给主线程。而如果任务耗时过长,则可能出现延迟导致主线程任务响应延迟。那么一种解决方法就是在主线程任务到来时将线程释放,先执行主线程任务,等主线程任务完成再继续执行该任务。这个理念最早在React的Schedule中实现,现在是浏览器要开始默认支持该功能。
在没有时间切片概念之前,其他的处理方法:setTimeout。
1 function blockingTask (ms = 200) { 2 let arr = []; 3 const blockingStart = performance.now(); 4 5 console.log(`Synthetic task running for ${ms} ms`); 6 7 while (performance.now() < (blockingStart + ms)) { 8 arr.push(Math.random() * performance.now / blockingStart / ms); 9 } 10 } 11 12 function yieldToMain () { 13 return new Promise(resolve => { 14 setTimeout(resolve, 0); 15 }); 16 } 17 18 async function runTaskQueueSetTimeout () { 19 if (typeof intervalId === "undefined") { 20 alert("Click the button to run blocking tasks periodically first."); 21 22 return; 23 } 24 25 clearTaskLog(); 26 27 for (const item of [1, 2, 3, 4, 5]) { 28 blockingTask(); 29 logTask(`Processing loop item ${item}`); 30 31 await yieldToMain(); 32 } 33 } 34 35 document.getElementById("setinterval").addEventListener("click", ({ target }) => { 36 clearTaskLog(); 37 38 intervalId = setInterval(() => { 39 if (taskOutputLines < MAX_TASK_OUTPUT_LINES) { 40 blockingTask(); 41 42 logTask("Ran blocking task via setInterval"); 43 } 44 }); 45 46 target.setAttribute("disabled", true); 47 }, { 48 once: true 49 }); 50 51 document.getElementById("settimeout").addEventListener("click", () => { 52 runTaskQueueSetTimeout(); 53 });
输出:
Processing loop item 1 Processing loop item 2 Ran blocking task via setInterval Processing loop item 3 Ran blocking task via setInterval Processing loop item 4 Ran blocking task via setInterval Processing loop item 5 Ran blocking task via setInterval Ran blocking task via setInterval
使用scheduler.yield
方法:
1 async function yieldy () { 2 // Do some work... 3 // ... 4 5 // Yield! 6 await scheduler.yield(); 7 8 // Do some more work... 9 // ... 10 }
示例:
1 async function runTaskQueueSchedulerDotYield () { 2 if (typeof intervalId === "undefined") { 3 alert("Click the button to run blocking tasks periodically first."); 4 5 return; 6 } 7 8 if ("scheduler" in window && "yield" in scheduler) { 9 clearTaskLog(); 10 11 for (const item of [1, 2, 3, 4, 5]) { 12 blockingTask(); 13 logTask(`Processing loop item ${item}`); 14 15 await scheduler.yield(); 16 } 17 } else { 18 alert("scheduler.yield isn't available in this browser :("); 19 } 20 }
输出:
Processing loop item 1 Processing loop item 2 Processing loop item 3 Processing loop item 4 Processing loop item 5 Ran blocking task via setInterval Ran blocking task via setInterval Ran blocking task via setInterval Ran blocking task via setInterval Ran blocking task via setInterval
该特性在谷歌115版本才支持,兼容写法:
1 function yieldToMain () { 2 // Use scheduler.yield if it exists: 3 if ('scheduler' in window && 'yield' in scheduler) { 4 return scheduler.yield(); 5 } 6 7 // Fall back to setTimeout: 8 return new Promise(resolve => { 9 setTimeout(resolve, 0); 10 }); 11 } 12 13 // Example usage: 14 async function doWork () { 15 // Do some work: 16 // ... 17 18 await yieldToMain(); 19 20 // Do some other work: 21 // ... 22 }
Processing loop item 1
Processing loop item 2
Ran blocking task via setInterval
Processing loop item 3
Ran blocking task via setInterval
Processing loop item 4
Ran blocking task via setInterval
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
FIGHTING