这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
前言
isInputPending()是FackBook与Google合作在Chrome浏览器上加入的一个Scheduling API,也是第一个将中断这个操作系统概念用于网页开发的API,开发者可以使用这个API来平衡JS执行、页面渲染及用户输入之间的优先级,就像系统使用中断调度CPU处理IO输入一样。
介绍
目前,isInputPending作为WICG的一个孵化标准,官方文档:Early detection of input events (wicg.github.io),其在Chrome 87 以上版本完全可用,
实测相同内核版本的Edge也没问题。
这个API是脸书为了权衡用户输入响应以及页面加载、脚本执行效率而制定的。当浏览器有需要处理的输入事件时,调用isInputPending()会返回true,在不传入任何参数的情况下,将会检测所有类型的输入事件,包括按键、鼠标、滚轮触控等DOM UI事件,也可以手动传入一个包含事件类型的数组参数。
浏览器特性检测:
if(navigator.scheduling.isInputPending){
// TODO
}
诞生背景
众所周知,JavaScript是一门单线程的语言,浏览器使用异步非阻塞的事件循环模型来调度JS任务。在任务执行的过程中,大多数任务都是在主线程中完成,对于UI渲染和脚本操作这两种互斥操作来说,单线程无疑是非常安全的。但主线程往往还要处理用户交互,如果脚本执行时间过长,则会导致无法及时处理用户响应,导致页面卡顿,用户体验降低。
以往我们将一些计算密集型或高延迟的任务放到Web Worker中执行,执行完毕后再通知主线程获取结果,但是Web Worker中不能操作dom,而动画绘制等一些UI操作也是一个非常耗时的操作,我们只能在主线程中处理。
目前对于JS调度的最佳实践是将需要长时间执行的JS任务分成一块一块,然后分别放入队列中依次执行,每次执行完后将控制权交还给浏览器,这时浏览器再看事件队列是否有需要响应的事件,全部响应完毕后再次执行JS任务,这类似于操作系统作业调度中的时间片轮转算法,每个任务都能公平得到处理。
不过,从上图看到,加载快和响应快是不可兼得的,如果JS执行时间太长,那么浏览器处理事件响应的的时机也会延迟:如果将JS任务分成一块一块执行以此来提高响应速度,那么页面的加载时间就会变长。
示例
检查任何输入事件
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending()) {
break;
}
let job = workQueue.shift();
job.execute();
}
检查特定输入事件
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending(['mousedown', 'mouseup', 'keydown', 'keyup'])) {
break;
}
let job = workQueue.shift();
job.execute();
}
总结
通过合理使用isInputPending方法,我们可以在页面渲染时及时响应用户输入,并且,当有长耗时的JS任务要执行时,可以通过isInputPending来中断JS的执行,将控制权交还给浏览器来执行用户响应。