You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
varmaxSigned31BitInt=1073741823;// Times out immediatelyvarIMMEDIATE_PRIORITY_TIMEOUT=-1;// 立马过期// Eventually times outvarUSER_BLOCKING_PRIORITY=250;// 250毫秒后过期varNORMAL_PRIORITY_TIMEOUT=5000;// 5秒后过期varLOW_PRIORITY_TIMEOUT=10000;// 10秒后过期// Never times outvarIDLE_PRIORITY=maxSigned31BitInt;// 永不过期
functionunstable_scheduleCallback(callback,deprecated_options){// 返回的是一个调用performance.now()的时间戳varstartTime=currentEventStartTime!==-1 ? currentEventStartTime : exports.unstable_now();// 过期时间varexpirationTime;if(typeofdeprecated_options==='object'&&deprecated_options!==null&&typeofdeprecated_options.timeout==='number'){expirationTime=startTime+deprecated_options.timeout;}else{// 根据任务优先级来设置任务的过期时间switch(currentPriorityLevel){caseImmediatePriority:
expirationTime=startTime+IMMEDIATE_PRIORITY_TIMEOUT;break;caseUserBlockingPriority:
expirationTime=startTime+USER_BLOCKING_PRIORITY;break;caseIdlePriority:
expirationTime=startTime+IDLE_PRIORITY;break;caseLowPriority:
expirationTime=startTime+LOW_PRIORITY_TIMEOUT;break;caseNormalPriority:
default:
expirationTime=startTime+NORMAL_PRIORITY_TIMEOUT;}}// 任务节点,varnewNode={callback: callback,// 具体的任务内容priorityLevel: currentPriorityLevel,// 当前任务的优先级expirationTime: expirationTime,// 当前任务的过期时间next: null,// 当前任务的下一个任务节点previous: null// 当前任务的上一个任务节点};// 向链表中插入任务节点,通过任务的过期时间来排序// 如果不存在任务节点,说明这是第一个任务,所以这个新的任务节点就是第一个任务节点,并且该节点的next和previous都指向自己,形成双循环结构。// 如果已经存在任务节点,那么就会通过任务的过期时间去排序,如果当前任务的过期时间大于新任务的过期时间,那么说明新任务的过期时间在当前任务之前,所以新任务会更快执行,所以会把新任务放在当前任务的前面if(firstCallbackNode===null){// This is the first callback in the list.firstCallbackNode=newNode.next=newNode.previous=newNode;ensureHostCallbackIsScheduled();}else{// 下一个任务的位置varnext=null;varnode=firstCallbackNode;do{if(node.expirationTime>expirationTime){// The new callback expires before this one.next=node;break;}node=node.next;}while(node!==firstCallbackNode);// 找了一圈发现,没有一个任务的过期时间比新的任务大,那么就说明新任务应该是在链表的最后,所以新任务的下一个任务就是第一个任务if(next===null){next=firstCallbackNode;}elseif(next===firstCallbackNode){// 找第一个的时候就找到了next,那么说明新的任务要放在第一个任务的前面firstCallbackNode=newNode;ensureHostCallbackIsScheduled();}// 任务之间的关联varprevious=next.previous;previous.next=next.previous=newNode;newNode.next=next;newNode.previous=previous;}returnnewNode;}
varrequestAnimationFrameWithTimeout=function(callback){rAFID=localRequestAnimationFrame(function(timestamp){// cancel the setTimeoutlocalClearTimeout(rAFTimeoutID);callback(timestamp);});rAFTimeoutID=localSetTimeout(function(){// cancel the requestAnimationFramelocalCancelAnimationFrame(rAFID);callback(exports.unstable_now());},ANIMATION_FRAME_TIMEOUT);};
react的调度机制
通过react v16版本的源码中,我们可以看到,对于react的任务调度的部分是放在schedular这个库中。不过这个调度机制是异步调度,目前我们使用react做的应用基本上都是同步的。
基础
在看schedular库中,有一些基础知识,我们需要先了解一下。
1、window.performance.now
这个方法表示的是从页面加载开始时,到当前之间的时间,单位为毫秒。
2、window.requestAnimationFrame
这个方法接受一个回调函数作为参数,表示的是推迟回调函数的执行,并且会推迟到浏览器在下一次重绘之前调用回调函数,也就是说该回调函数会在浏览器下一次重绘之前执行。(回调函数会接受一个参数,这个参数是一个时间戳,是performance.now()的返回值,表示的是从页面加载开始到当前的时间)。
如果想让浏览器在下一次重绘之前继续更新下一帧动画,那么可以在回调函数中再次调用window.requestAnimationFrame方法。
3、window.MessageChannel
这个接口允许我们创建一个新的消息通道,并通过它的两个MessagePort属性发送数据。
用法:
调度
1、任务优先级
react对任务的优先级分为五种:
五种优先级所对应的过期时间:
添加任务
react添加任务,我们需要了解,它是怎么添加的,添加到哪里的?我们来具体看一下代码中是怎么实现的:
上面的代码就是如何插入任务,并且通过任务的过期时间,将这些任务都进行了排序,那任务是什么时候执行的呢?
通过上面代码中,我们可以看到有两种情况,第一种情况是:当添加第一个任务节点的时候开始启动任务执行,第二种情况是:当新添加的任务节点取代之前的任务节点称为新的第一个节点的时候,也会启动任务执行。这就是源码中的==ensureHostCallbackIsScheduled==方法执行的内容。
因为第一种情况表示的是,任务从无到有,所以应该立即执行。第二种情况表示的是,有新的优先级最高的任务,应该停止之前要执行的任务,重新从新的任务开始执行。
如何在浏览器每一帧绘制完的空闲时间来做一些事情?react使用的是requestAnimationFrame和MessageChannel来实现。
requestAnimationFrameWithTimeout方法
上面这段代码是什么意思呢?其实就是当我们调用requestAnimationFrameWithTimeout方法,并且传入一个callback参数的时候,会启动一个requestAnimationFrame和一个setTimeout,两个都会执行,但是由于requestAnimationFrame的执行优先级高于setTimeout,所以会先执行requestAnimationFrame,当执行requestAnimationFrame的时候,会调用localClearTimeout方法(其实就是clearTimeout方法)取消setTimeout定时器的执行,所以在页面激活的情况下,其实执行的就是requestAnimationFrame。
但是requestAnimationFrame方法,在页面切换到未激活的时候是不工作的,这时候requestAnimationFrameWithTimeout方法其实就启动了一个100毫秒的定时器,来执行任务。
ensureHostCallbackIsScheduled方法。
当我们将任务通过过期时间进行排序添加到链表中后,我们就要在合适的时机去执行这些任务,这里我们会调用ensureHostCallbackIsScheduled方法。
创建消息通道
根据上面代码,flushWork方法,会根据didTimeout有两种处理情况,如果didTimeout为true,就会把任务链表中的所有过期任务都执行一遍,如果didTimeout为false,那么会在当前帧过期之前尽可能多的去执行链表中的任务。
上面代码中,有两层循环,我们先来看里面这层循环,如果链表上有任务,并且任务已过期,那么就会执行这个任务,直到链表上没有任务或者链表上的任务没有过期为止,这个时候就会又执行外层的循环,外层循环中,又会重新获取一次当前时间,然后再去判断任务是否过期,如果过期,那么就继续执行内层循环,如果没有过期,那么就会推出外层循环,继续执行后面代码。
总结
react的任务调度流程:
Life of a frame
一帧里面除了上面图片中干的活之外就是空闲时间了。
The text was updated successfully, but these errors were encountered: