React16源码: React中的异步调度scheduler模块的源码实现

PID不够用?试试用ADRC改造你的ArduCopter:从Simulink仿真到SITL测试全记录 本文详细介绍了如何将ADRC(自抗扰控制)算法集成到ArduPilot飞控中,以替代传统PID控制器。通过Simulink建模、参数整定和SITL仿真测试,展示了ADRC在抗扰动能力、响应速度和参数调节复杂度上的优势,特别适合无人机在强风扰动下的稳定控制。 阅读详情

React Scheduler


1 ) 概述

  • react当中的异步调度,称为 React Scheduler
  • 发布成单独的一个 npm 包就叫做 scheduler
  • 这个包它做了什么?
    • A. 首先它维护时间片
    • B. 然后模拟 requestIdleCallback 这个API
      • 因为现在浏览器的支持不是特别的多
      • 所以在浏览当中只是去模拟了一个这个API,而不是直接使用这个API
      • 因为需要考虑到浏览器兼容性
      • 这个API的作用
        • 调用这个API传入一个回调之后,这个API会等到浏览器把它的一些主要任务执行完了
        • 当它有空闲的时间的时候,再回来调用这个回调
      • 相对于 requestAnimationFrame 来说,它的优先级会低很多
      • 它是等浏览器器要做的事情做完了之后,再回来调这个回调
      • 而 requestAnimationFrame 是浏览器要渲染当前帧的时候,调用这个回调
    • C. 调度列表和进行一个超时的判断
  • 关于时间片
    • 不管是在浏览器还是在App当中,要给用户很流畅的一个感觉的时候
    • 至少要保证在一秒钟之内要渲染30帧以上
    • 现在的一些高刷新率的浏览器,可能会要求在60帧以上,甚至还有更高的,比如,120帧
    • 这个帧数就是我们1秒钟,页面要重新渲染刷新多少次
    • 它并不是说我一秒钟之内刷新30次,满足就行了。
    • 比如前面的半秒钟只刷新了一次,后面的半秒钟刷新了二十九次,这个也是不行的
    • 这个给用户的感觉,就是前面这半秒钟会特别的卡就一动不动,然后后面又变得流畅
    • 所以,它的要求还需要是平均的每33毫秒要刷新1帧,要保持这个频率
    • 浏览器必须自己去渲染这些动画,要每1帧里面有固定的时间去渲染这个动画
    • 在这里举个例子,比如说整个应用所有的js的操作,都是通过 react 来实现的
    • 而浏览器有一个一直在更新的动画, 浏览器渲染这个动画如果要11毫秒
    • 那么给每一帧的, 就是把一秒钟分成了30帧之后,每一帧是33毫秒
    • 这个33毫秒里面的11毫秒是必须要留给浏览器去渲染这个动画的, 才能让这个动画看起来是流畅的
    • 而在这个时候留给react去渲染它的应用更新的时候,每一帧里面就只有22毫秒
    • 如果react它在这一帧里面的一个更新,它需要渲染的时间很长,比如说35毫秒
    • 那这个时候,我们一帧的时间就全部给react渲染给占掉了
    • 因为 js 引擎是单线程的, 如果react在一直在执行,浏览器它就没有机会去获得运行权
    • 就没有机会去刷新它的一个动画, 这时候,不仅把一帧的时间占完了
    • 这样还不够,还要去下一帧里面借用一点时间,那么这个时间用完之后
    • 浏览器要去更新动画,如果这一帧里面我们就用掉了13毫秒,剩下的时间就只剩下20毫秒
    • 那么这20毫秒,又可能要运行一部分react的更新,然后再去浏览器的一个渲染
    • 这就会导致整个动画变得卡顿起来了
    • 这就是 React Scheduler 它的一个目的, 为了保证react它去执行更新的这个时间
    • 不超过在浏览器的每一帧里面特定的时间,它希望留给浏览器去刷新动画,或者是响应用户输入的反馈的时候
    • 每一帧里面有足够的时间

2 )时间片源码

  • 时间片源码在 packages/scheduler 这个包里面,是一个单独的模块,单独发布到 npm 上

  • 在 ReactFiberScheduler.js 里面,哪个地方用到它呢?

    • 在 requestWork 函数里面,如果 expirationTime 异步的,就会调用 scheduleCallbackWithExpirationTime
      function scheduleCallbackWithExpirationTime(
        root: FiberRoot,
        expirationTime: ExpirationTime,
      ) {
        if (callbackExpirationTime !== NoWork) {
          // A callback is already scheduled. Check its expiration time (timeout).
          if (expirationTime > callbackExpirationTime) {
            // Existing callback has sufficient timeout. Exit.
            return;
          } else {
            if (callbackID !== null) {
              // Existing callback has insufficient timeout. Cancel and schedule a
              // new one.
              cancelDeferredCallback(callbackID);
            }
          }
          // The request callback timer is already running. Don't start a new one.
        } else {
          startRequestCallbackTimer();
        }
      
        callbackExpirationTime = expirationTime;
        const currentMs = now() - originalStartTimeMs;
        const expirationTimeMs = expirationTimeToMs(expirationTime);
        const timeout = expirationTimeMs - currentMs;
        callbackID = scheduleDeferredCallback(performAsyncWork, {timeout});
      }
      
      • 全局变量 callbackExpirationTime 对应的是 上一次调用 React Scheduler 去申请了一个callback
      • 这个callback 也会有一个 expirationTime, 因为是异步调度,所以会有一个 expirationTime 传进来
      • 如果这个 callbackExpirationTime !== NoWork 代表之前有一个callback在执行了
      • 这边就会判断当前的 expirationTime 是否比之前回调中的那个要大
      • 如果大,说明当前的这个的优先级要低,这个时候就直接return了不执行
      • 因为它优先级更低,我们肯定要执行优先级更高的那个,调用 cancelDeferredCallback 把之前的 cancel 掉
      • startRequestCallbackTimer 这个函数跳过,不涉及主流程,涉及DEV Tool 相关
      • 接着更新一系列的变量
        • 更新 callbackExpirationTime
        • 计算出 timeout
      • 最后调用 scheduleDeferredCallback 这个方法来自于 ReactFiberHostConfig.js
        • 如果直接查找 这个文件,发现基本上没有什么内容, 是因为 React对于打包工具的配置,进行了文件名的映射
        • 它实际映射的是 eact-reconciler/src/forks/ReactFiberHostConfig.dom.js
          export * from 'react-dom/src/client/ReactDOMHostConfig';
          
        • 发现里面就一行代码,找到对应的 ReactDOMHostConfig.js 文件,搜索 scheduleDeferredCallback 方法
          export {
            unstable_scheduleCallback as scheduleDeferredCallback,
          } from 'scheduler';
          
          • 可追溯到 这个方法来自于 scheduler 包
          • 这个方法涉及比较多,先跳过
        • callbackID = scheduleDeferredCallback(performAsyncWork, {timeout});
      • 它最后返回 一个 callbackID, 这个id用于后期 cancel 的标识,cancelDeferredCallback(callbackID);
        • 这里之前也说了,如果新的任务优先级更高,需要把老的取消,再调用新的callback
      • 而里面的参数 performAsyncWork
        • 在 requestWork 中,当 expirationTime === Sync 时,调用的也是 performSyncWork 这个是同步的
        • 而如果是异步,则调用 scheduleCallbackWithExpirationTime 函数,最终调用的是这里的 performAsyncWork
        • 所以,这两个是对应的,同步和异步
  • 进入 scheduleDeferredCallback 函数的源码 packages/scheduler/src/Scheduler.js 找到 unstable_scheduleCallback

    function unstable_scheduleCallback(callback, deprecated_options) {
      var startTime =
        currentEventStartTime !== -1 ? currentEventStartTime : getCurrentTime();
    
      var expirationTime;
      if (
        typeof deprecated_options === 'object' &&
        deprecated_options !== null &&
        typeof deprecated_options.timeout === 'number'
      ) {
        // FIXME: Remove this branch once we lift expiration times out of React.
        expirationTime = startTime + deprecated_options.timeout;
      } else {
        switch (currentPriorityLevel) {
          case ImmediatePriority:
            expirationTime = startTime + IMMEDIATE_PRIORITY_TIMEOUT;
            break;
          case UserBlockingPriority:
            expirationTime = startTime + USER_BLOCKING_PRIORITY;
            break;
          case IdlePriority:
            expirationTime = startTime + IDLE_PRIORITY;
            break;
          case NormalPriority:
          default:
            expirationTime = startTime + NORMAL_PRIORITY_TIMEOUT;
        }
      }
    
      var newNode = {
        callback,
        priorityLevel: currentPriorityLevel,
        expirationTime,
        next: null,
        previous: null,
      };
    
      // Insert the new callback into the list, ordered first by expiration, then
      // by insertion. So the new callback is inserted any other callback with
      // equal expiration.
      if (firstCallbackNode === null) {
        // This is the first callback in the list.
        firstCallbackNode = newNode.next = newNode.previous = newNode;
        ensureHostCallbackIsScheduled();
      } else {
        var next = null;
        var node = 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) {
          // No callback with a later expiration was found, which means the new
          // callback has the latest expiration in the list.
          next = firstCallbackNode;
        } else if (next === firstCallbackNode) {
          // The new callback has the earliest expiration in the entire list.
          firstCallbackNode = newNode;
          ensureHostCallbackIsScheduled();
        }
    
        var previous = next.previous;
        previous.next = next.previous = newNode;
        newNode.next = next;
        newNode.previous = previous;
      }
    
      return newNode;
    }
    
    • 首先看 参数 callback, deprecated_options
      • callback 是传进来的 performAsyncWork
      • deprecated_options 是即将被废弃的 optinos,这个即将被废弃
    • 接着处理 var startTime = currentEventStartTime !== -1 ? currentEventStartTime : getCurrentTime();
      • getCurrentTime 是重新计算一个 xx.now()
        if (hasNativePerformanceNow) {
          var Performance = performance;
          getCurrentTime = function() {
            return Performance.now();
          };
        } else {
          getCurrentTime = function() {
            return localDate.now();
          };
        }
        
      • 这里,浏览器平台是这个 localDate.now();
    • 下面有个判断if (typeof deprecated_options === 'object' && deprecated_options !== null && typeof deprecated_options.timeout === 'number')
      • 接着判断 deprecated_options 这个参数,存在则计算出 expirationTime
        // FIXME: Remove this branch once we lift expiration times out of React.
        expirationTime = startTime + deprecated_options.timeout;
        
        • 当把 expirationTime 相关的逻辑提取出来之后,这个 if判断就被删除了,后面只有 else 里面的东西了
        • 所以说,这个 deprecated_options 即将被废弃
    • 如果走到 else 里面,进行switch case currentPriorityLevel
      • 可以看下各个常量的值
        var maxSigned31BitInt = 1073741823;
        
        // Times out immediately
        var IMMEDIATE_PRIORITY_TIMEOUT = -1;
        // Eventually times out
        var USER_BLOCKING_PRIORITY = 250;
        var NORMAL_PRIORITY_TIMEOUT = 5000;
        // Never times out
        var IDLE_PRIORITY = maxSigned31BitInt;
        
      • 也就是说,将来很可能会把 expirationTime 相关逻辑移入 scheduler 包中
      • 之前在 packages/react-reconciler/src/ReactFiberReconciler.js 中
      • 不过,在目前的逻辑中 else 里面的东西,用不到
    • 接下去,创建 newNode 的对象
      var newNode = {
        callback,
        priorityLevel: currentPriorityLevel,
        expirationTime,
        next: null,
        previous: null,
      };
      
      • next 和 previous 是用来存储链表的数据结构的
    • 接下来 if (firstCallbackNode === null)
      • firstCallbackNode 是 scheduler 中维护的一个单项列表的头部
      • 如果匹配判断,说明传递进来的 callback 是第一个
        • 进行赋值处理 firstCallbackNode = newNode.next = newNode.previous = newNode;
        • 并调用 ensureHostCallbackIsScheduled();
    • 不匹配的时候
      • 有一个或多个callback, 则进行循环
      • 在循环中判断,node.expirationTime > expirationTime
        • 如果匹配,next = node; 并跳出循环
        • 这是 scheduler 对于传进来的所有callback, 按照 expirationTime 的大小,也就是优先级的高低进行排序
        • 它会把优先级更高的任务,排到最前面
      • 如果 next 是 null
        • 这个节点要插在callbackList里面的最后一个
      • 如果 next 是 firstCallbackNode,即第一个
        • 因为当前节点要插在这个单项列表最前面,优先级最高
        • 马上 firstCallbackNode 变化了,即更新了 firstCallbackNode = newNode;
        • 调用 ensureHostCallbackIsScheduled();
          • 这个函数在上面两处调用了,但是没有在 if (next === null) 中调用
            • 因为 这个条件下,firstCallbackNode 仍然处于第一位
            • 后续要调用的话,第一个被调用的还是 firstCallbackNode
            • 所以,顺序不会变,所以不需要重新调用 ensureHostCallbackIsScheduled();
          • 注意,调用上述方法会进入一个循环,循环的调用List里面的东西
          • 当 firstCallbackNode 变化了,才会去调用,因为头部变了
  • 下面为这个方法链表的处理示例

  • 接着,进入 ensureHostCallbackIsScheduled 这个方法让队列进入调度的过程
    function ensureHostCallbackIsScheduled() {
      if (isExecutingCallback) {
        // Don't schedule work yet; wait until the next time we yield.
        return;
      }
      // Schedule the host callback using the earliest expiration in the list.
      var expirationTime = firstCallbackNode.expirationTime;
      if (!isHostCallbackScheduled) {
        isHostCallbackScheduled = true;
      } else {
        // Cancel the existing host callback.
        cancelHostCallback();
      }
      requestHostCallback(flushWork, expirationTime);
    }
    
    • isExecutingCallback 变量表示已经调用callback, 直接 return
      • 代表着 已经有一个callbackNode 被调用了
      • 也就是我们传入的 performAsyncWork 正在被调用了
      • 进入被调用的过程,自动进入一个循环的过程
      • 就不需要再重新启动一次调度
    • 获取变量 var expirationTime = firstCallbackNode.expirationTime;
    • 如果没有被调度,标识正在被调度 isHostCallbackScheduled = true;
    • 否则,取消之前的回调 cancelHostCallback();
    • 最后 requestHostCallback, 进入这个方法,有很多种场景分别定义,但是找到我们需要的场景,搜索该方法名
      • 排除 mock, 非浏览器环境的判断
      • 并进入直接到 else 中
        // 这里跳过很多代码
        // ...
        // 主要在这里
        requestHostCallback = function(callback, absoluteTimeout) {
          scheduledHostCallback = callback;
          timeoutTime = absoluteTimeout;
          if (isFlushingHostCallback || absoluteTimeout < 0) {
            // Don't wait for the next frame. Continue working ASAP, in a new event.
            window.postMessage(messageKey, '*');
          } else if (!isAnimationFrameScheduled) {
            // If rAF didn't already schedule one, we need to schedule a frame.
            // TODO: If this rAF doesn't materialize because the browser throttles, we
            // might want to still have setTimeout trigger rIC as a backup to ensure
            // that we keep performing work.
            isAnimationFrameScheduled = true;
            requestAnimationFrameWithTimeout(animationTick);
          }
        };
        
        cancelHostCallback = function() {
          scheduledHostCallback = null;
          isMessageEventScheduled = false;
          timeoutTime = -1;
        };
        
    • 上述 requestHostCallback 是我们需要关注的点
      • cheduledHostCallback = callback; 读取 callback
      • timeoutTime = absoluteTimeout; 是我们传进来的 expirationTime
      • 接着判断 if (isFlushingHostCallback || absoluteTimeout < 0)
        • 这两种情况,不需要等待下一帧去做这个事情
        • 而是以最快的速度进入这个方法的调用 window.postMessage(messageKey, '*');
        • absoluteTimeout < 0 说明已经超时了
      • 不符合上述条件,按照正常的调度流程去走
        • 判断 isAnimationFrameScheduled 这个变量的状态
        • 如果它没有设置为 true, 则还没有进入调度循环的过程
        • 这时候就把它设置为 true, 并执行 requestAnimationFrameWithTimeout(animationTick);
        • 进入 requestAnimationFrameWithTimeout
          var requestAnimationFrameWithTimeout = function(callback) {
            // schedule rAF and also a setTimeout
            rAFID = localRequestAnimationFrame(function(timestamp) {
              // cancel the setTimeout
              localClearTimeout(rAFTimeoutID);
              callback(timestamp);
            });
            rAFTimeoutID = localSetTimeout(function() {
              // cancel the requestAnimationFrame
              localCancelAnimationFrame(rAFID);
              callback(getCurrentTime()); // 这里 getCurrentTime 是一个模拟 timestamp 的参数
            }, ANIMATION_FRAME_TIMEOUT);
          };
          
          • 这里的 localRequestAnimationFrame 相当于 window.requestAnimationFrame
            • 它内部做了两件事,清理 timeout, 执行callback
            • 这个 callback 就是我们传进来的 animationTick 这个方法
            • rAFTimeoutID 是下面的 timeout 定时器
            • 这个定时器的作用是: 如果 localRequestAnimationFrame 一直没有调用,超时了,这边设置的时间是 100ms
            • 超时后,取消 localRequestAnimationFrame 的调用,并且直接调用 callback(getCurrentTime());
            • 也就是下一帧的时间必须在 100ms之内被调用
            • 这个方法的作用就是,防止 localRequestAnimationFrame 太长时间没有被调用
            • 里面有相互取消的操作,这里面有一个竞争关系,谁先触发,谁先调用
        • 同样,参数这里 animationTick 也是个方法
          var animationTick = function(rafTime) {
            // 这里会匹配到
            if (scheduledHostCallback !== null) {
              // Eagerly schedule the next animation callback at the beginning of the
              // frame. If the scheduler queue is not empty at the end of the frame, it
              // will continue flushing inside that callback. If the queue *is* empty,
              // then it will exit immediately. Posting the callback at the start of the
              // frame ensures it's fired within the earliest possible frame. If we
              // waited until the end of the frame to post the callback, we risk the
              // browser skipping a frame and not firing the callback until the frame
              // after that.
              // 因为 firstCallbackNode 是一个队列,里面会有很多 callback
              // 当前 animationTick 只执行一个 callback
              // 如果后续还有,也会在下一帧中去执行
              // 不期望等待callback执行完成后,再去请求下一帧,可能会跳过很多的时间
              // 所以在这里立马执行
              requestAnimationFrameWithTimeout(animationTick); // 立即进行调用,请求下一帧
            } else {
              // No pending work. Exit.
              // 如果下次进来,scheduledHostCallback 是没有的,则跳出
              isAnimationFrameScheduled = false;
              return;
            }
            // rafTime 是 animationTick 被调用的时间
            // frameDeadline 默认是 0
            // activeFrameTime 是 33,这个就是保持浏览器30帧的执行时间
            // 这里就是计算,这个方法到下一帧可以执行的时间
            var nextFrameTime = rafTime - frameDeadline + activeFrameTime;
            if (
              nextFrameTime < activeFrameTime &&
              previousFrameTime < activeFrameTime
            ) {
              if (nextFrameTime < 8) {
                // Defensive coding. We don't support higher frame rates than 120hz.
                // If the calculated frame time gets lower than 8, it is probably a bug.
                nextFrameTime = 8;
              }
              // If one frame goes long, then the next one can be short to catch up.
              // If two frames are short in a row, then that's an indication that we
              // actually have a higher frame rate than what we're currently optimizing.
              // We adjust our heuristic dynamically accordingly. For example, if we're
              // running on 120hz display or 90hz VR display.
              // Take the max of the two in case one of them was an anomaly due to
              // missed frame deadlines.
              activeFrameTime =
                nextFrameTime < previousFrameTime ? previousFrameTime : nextFrameTime;
            } else {
              previousFrameTime = nextFrameTime;
            }
            frameDeadline = rafTime + activeFrameTime;
            if (!isMessageEventScheduled) {
              isMessageEventScheduled = true;
              window.postMessage(messageKey, '*');
            }
          };
          
          • 第一次计算的 nextFrameTime 其实是没有用的,因为算出来的时间会比较大
          • 第二次进来,这时候 frameDeadline 就不是 0 了
          • 而 requestAnimationFrameWithTimeout 又是连续调用的
          • 因为我们进入这个方法就会立马调用这个方法
          • 下个方法调用就是下一帧了,因为 requestAnimationFrame 是一帧一帧来调用的
          • 下一帧时间进来,又重新计算出来了一个 nextFrameTime
          • 这个时候,rafTime 是小于 frameDeadline 的,因为 frameDeadline 加上了一个完整帧的时间 33
          • 对于调用 requestAnimationFrame 的时候,是下一帧动画刚开始渲染的时候,肯定没有到 33 毫秒的时候
          • 这时候 nextFrameTime 是小于 33,说明机器的刷新频率高于30帧
          • if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime)
          • 这个判断的意义在于,如果连续两帧的调用都计算出来,发现小于 33 ms (目前的帧时间)
          • 那么就把帧时间 activeFrameTime 变小,因为使用 frameDeadline 的时候,activeFrameTime
          • 是非常重要的,frameDeadline = rafTime + activeFrameTime;
          • 说明在接下去的 33ms之内都是可以运行react更新的代码
          • 实际浏览器的刷新时间都要小于33ms, 比如 10ms, 这时候,占用33ms去渲染react应用
          • 就会导致浏览器刷新动画的时间,非常不够,就导致动画变得比较卡顿
          • 这个是考虑不同平台刷新频率的问题,不如 VR平台对刷新要求比较高
          • 如果 nextFrameTime < 8 这时候 nextFrameTime = 8
          • 这说明react目前不支持每帧小于8ms的场景
          • 通过以上前后几次帧时间的判断,来判断平台的刷新频率来更新 activeFrameTime
          • 来减少 react 运行时间的目的
          • 但是 frameDeadline = rafTime + activeFrameTime; 这里计算出的 frameDeadline 要大于33的
            • 因为 activeFrameTime 是完整的一帧时间 33
            • 而每帧留给 react 更新的时间要小于 33
            • 一帧之内要处理 react的渲染 和 浏览器的更新,那么 react渲染一定要小于33
              这里算出的 frameDeadline 是 当前时间 + 33
          • 这是为什么呢?
            • 这里用了js中任务队列的概念,像是 setTimeout, window.postMessage
            • 都是把一个任务推到了一个队列里面, 然后再继续执行当前 js 的任务
            • 对于浏览器来说,animationTick 是在 requestAnimationFrameWithTimeout 的callback中调用
            • animationTick 方法执行完之后,立马进入浏览器动画刷新的流程
            • 下面调用的 window.postMessage 要等到浏览器动画或用户反馈执行完了之后,才会执行 postMessage 的功能
            • 这意味着需要等到浏览器刷新完成后,才会接收到 postMessage 的意图
            • 这时候浏览器刷新动画的时间已经过了,相当于 rafTime + activeFrameTime 的时间已经流失掉一部分了(浏览器刷新需时)
            • 剩下的时间给 react 执行更新的
          • 这就是 react scheduler 中模拟 requestIdleCallback 的方法,通过 requestAnimationFrame 调用完 callback 之后
          • 立马进入浏览器的动画更新的设定,在下面的判断中给任务队列插入一个任务
          • if (!isMessageEventScheduled)
            • isMessageEventScheduled = true;
            • window.postMessage(messageKey, '*');
          • 在浏览器执行完之后,调用任务队列, 这个时间总共加起来是 33ms
          • 当发送完 postMessage 到了哪里?可看到
            window.addEventListener('message', idleTick, false)
            
          • 进入 idleTick
            var idleTick = function(event) {
              // 先判断 key
              if (event.source !== window || event.data !== messageKey) {
                return;
              }
            
              isMessageEventScheduled = false;
              // 赋值一份 callback
              var prevScheduledCallback = scheduledHostCallback;
              // 同样处理 timeout
              var prevTimeoutTime = timeoutTime;
              // 重置下面两个
              scheduledHostCallback = null;
              timeoutTime = -1;
              // 获取当前时间
              var currentTime = getCurrentTime();
            
              var didTimeout = false;
              // 这个条件如果 <= 0 说明浏览器动画或用户反馈超过 33ms, 意思是,把这一帧的时间已经用完了
              // 对于 react 来说,它已经没有时间执行它的更新了
              if (frameDeadline - currentTime <= 0) {
                // There's no time left in this idle period. Check if the callback has
                // a timeout and whether it's been exceeded.
                // 进入上述条件,它需要继续判断 timeout 是否已经过期,或者小于当前时间(说明任务也已经过期了)
                // 如果任务已经过期,这个任务就需要强行被更新
                // 可以在任务没有过期的时候,判断帧时间如果没有了,即: frameDeadline - currentTime <= 0
                // 先跳过,等下一帧来更新,但是在任务已经过期的时候,就需要强制执行了,于是就设置了下面的 didTimeout = true;
                if (prevTimeoutTime !== -1 && prevTimeoutTime <= currentTime) {
                  // Exceeded the timeout. Invoke the callback even though there's no
                  // time left.
                  didTimeout = true;
                } else {
                  // No timeout.
                  // 没有过期,并且 isAnimationFrameScheduled === false 去调用 requestAnimationFrameWithTimeout 这个方法
                  if (!isAnimationFrameScheduled) {
                    // Schedule another animation callback so we retry later.
                    // 恢复
                    isAnimationFrameScheduled = true;
                    requestAnimationFrameWithTimeout(animationTick);
                  }
                  // Exit without invoking the callback.
                  scheduledHostCallback = prevScheduledCallback;
                  timeoutTime = prevTimeoutTime;
                  return;
                }
              }
              // 接来下如果 存在 prevScheduledCallback 则设置 isFlushingHostCallback 并调用 prevScheduledCallback
              if (prevScheduledCallback !== null) {
                isFlushingHostCallback = true;
                try {
                  prevScheduledCallback(didTimeout);
                } finally {
                  isFlushingHostCallback = false;
                }
              }
            };
            
            • 这里最后的 prevScheduledCallback 向上溯源,找到 ensureHostCallbackIsScheduled
            function ensureHostCallbackIsScheduled() {
              if (isExecutingCallback) {
                // Don't schedule work yet; wait until the next time we yield.
                return;
              }
              // Schedule the host callback using the earliest expiration in the list.
              var expirationTime = firstCallbackNode.expirationTime;
              if (!isHostCallbackScheduled) {
                isHostCallbackScheduled = true;
              } else {
                // Cancel the existing host callback.
                cancelHostCallback();
              }
              requestHostCallback(flushWork, expirationTime);
            }
            
            • 可以看到这里 requestHostCallback(flushWork, expirationTime); 传入了 flushWork 方法
              • 输出任务需要调用 flushWork 方法
            • 现在来看下 flushWork 方法, 这是 react scheduler 调度到 要执行 callback 的流程
            • 在执行callback的时候,调用了 flushWork 这个方法
              // didTimeout 参数是 firstCallbackNode 的 expirationTime 是否已超时
              function flushWork(didTimeout) {
                // 真正调用 callback 设置为 true
                // 对于 ensureHostCallbackIsScheduled 方法来说,如果为 true, 则直接 return 了
                isExecutingCallback = true;
                deadlineObject.didTimeout = didTimeout; // deadlineObject 是上层设置的一个通用的对象
                try {
                  if (didTimeout) {
                    // Flush all the expired callbacks without yielding.
                    while (firstCallbackNode !== null) {
                      // Read the current time. Flush all the callbacks that expire at or
                      // earlier than that time. Then read the current time again and repeat.
                      // This optimizes for as few performance.now calls as possible.
                      var currentTime = getCurrentTime();
                      if (firstCallbackNode.expirationTime <= currentTime) {
                        // 执行 callbackNode 的链表直到遇到第一个不过期的为止,把已过期的任务都强制输出
                        do {
                          flushFirstCallback();
                        } while (
                          firstCallbackNode !== null &&
                          firstCallbackNode.expirationTime <= currentTime
                        ); // 这里 firstCallbackNode 是 next, firstCallbackNode.expirationTime <= currentTime 这表示下一个节点的任务还是过期的任务
                        continue; // 这里continue 跳出后即 break 跳出 外层while循环
                      }
                      break;
                    }
                  } else {
                    // 这里表示没有任务是过期的
                    // Keep flushing callbacks until we run out of time in the frame.
                    if (firstCallbackNode !== null) {
                      do {
                        flushFirstCallback();
                      } while (
                        firstCallbackNode !== null &&
                        getFrameDeadline() - getCurrentTime() > 0
                      ); // getFrameDeadline() - getCurrentTime() > 0 表示 有空,闲暇 会执行 flushFirstCallback
                    }
                  }
                } finally {
                  isExecutingCallback = false;
                  // 最后,如果还有,再次进入调度
                  if (firstCallbackNode !== null) {
                    // There's still work remaining. Request another callback.
                    ensureHostCallbackIsScheduled();
                  } else {
                    isHostCallbackScheduled = false;
                  }
                  // Before exiting, flush all the immediate work that was scheduled.
                  flushImmediateWork();
                }
              }
              
              • 关于这里的 deadlineObject
              var deadlineObject = {
                timeRemaining,
                didTimeout: false,
              };
              
              • 而这里的 timeRemaining 是一个方法
              var timeRemaining;
              if (hasNativePerformanceNow) {
                timeRemaining = function() {
                  // 这个判断不成立,跳过
                  if (
                    firstCallbackNode !== null &&
                    firstCallbackNode.expirationTime < currentExpirationTime
                  ) {
                    // A higher priority callback was scheduled. Yield so we can switch to
                    // working on that.
                    return 0;
                  }
                  // We assume that if we have a performance timer that the rAF callback
                  // gets a performance timer value. Not sure if this is always true.
                  // getFrameDeadline() 方法就是 frameDeadline = rafTime + activeFrameTime;
                  // 也就是说,确定,这一帧的渲染时间,是否已经超过
                  var remaining = getFrameDeadline() - performance.now();
                  return remaining > 0 ? remaining : 0;
                };
              } else {
                timeRemaining = function() {
                  // Fallback to Date.now()
                  if (
                    firstCallbackNode !== null &&
                    firstCallbackNode.expirationTime < currentExpirationTime
                  ) {
                    return 0;
                  }
                  var remaining = getFrameDeadline() - Date.now();
                  return remaining > 0 ? remaining : 0;
                };
              }
              
              • 这里根据 hasNativePerformanceNow 来进行一个区分,这两个基本差不多,选择其一
              • 所以,timeRemaining 用于计算还剩多少时间,比如在 ReactFiberSchedler.js中的 shouldYield
                // When working on async work, the reconciler asks the renderer if it should
                // yield execution. For DOM, we implement this with requestIdleCallback.
                function shouldYield() {
                  if (deadlineDidExpire) {
                    return true;
                  }
                  if (
                    deadline === null ||
                    deadline.timeRemaining() > timeHeuristicForUnitOfWork
                  ) {
                    // Disregard deadline.didTimeout. Only expired work should be flushed
                    // during a timeout. This path is only hit for non-expired work.
                    return false;
                  }
                  deadlineDidExpire = true;
                  return true;
                }
                
                • 这里 deadline.timeRemaining() > timeHeuristicForUnitOfWork, 这里 timeHeuristicForUnitOfWork 是 1
                • 用剩下时间是否 > 1 来判断是否已经过期了,如果 > 1 说明还有时间执行 react的更新,这里 return false
                • 如果剩下时间 < 1 了,代表这一帧的渲染时间已经超时,设置全局变量 deadlineDidExpire = true; 并且 return true
                • 这个 shouldYield 方法就是判断这个任务要跳出还是继续执行下去
              • 接着是一个 try finally 里面
                • if (didTimeout) 这时候已经过期了,进入while循环 while (firstCallbackNode !== null)
                • 里面 有个if 判断是肯定匹配的,if (firstCallbackNode.expirationTime <= currentTime)
                  • 执行 do while
                  • flushFirstCallback 是真正调用callback的方法
                    function flushFirstCallback() {
                      var flushedNode = firstCallbackNode;
                    
                      // Remove the node from the list before calling the callback. That way the
                      // list is in a consistent state even if the callback throws.
                      var next = firstCallbackNode.next;
                      // 说明链表里只有一个节点,直接设置 null
                      if (firstCallbackNode === next) {
                        // This is the last callback in the list.
                        firstCallbackNode = null;
                        next = null;
                      } else {
                        // 如果不是,多个节点,则构建链表(环形链表) 
                        var lastCallbackNode = firstCallbackNode.previous;
                        firstCallbackNode = lastCallbackNode.next = next; // firstCallbackNode 变成了 next 的节点 对应上面调用方法的 do while firstCallbackNode.exirationTime <= currentTime 
                        next.previous = lastCallbackNode;
                      }
                      // 把之前的指向清空,如果指针还留着,可能会导致问题
                    
                      flushedNode.next = flushedNode.previous = null;
                    
                      // Now it's safe to call the callback.
                      var callback = flushedNode.callback;
                      var expirationTime = flushedNode.expirationTime;
                      var priorityLevel = flushedNode.priorityLevel;
                      var previousPriorityLevel = currentPriorityLevel;
                      var previousExpirationTime = currentExpirationTime;
                      currentPriorityLevel = priorityLevel;
                      currentExpirationTime = expirationTime;
                      var continuationCallback;
                      try {
                        // 这里 callback 就是传进来的 performAsyncWork
                        continuationCallback = callback(deadlineObject); // 这里应该是 undefined
                      } finally {
                        currentPriorityLevel = previousPriorityLevel;
                        currentExpirationTime = previousExpirationTime;
                      }
                    
                      // A callback may return a continuation. The continuation should be scheduled
                      // with the same priority and expiration as the just-finished callback.
                      // 因为 performAsyncWork 这个 callback 没有返回值 所以这个目前来说不成立,也许后续会有用
                      if (typeof continuationCallback === 'function') {
                        var continuationNode: CallbackNode = {
                          callback: continuationCallback,
                          priorityLevel,
                          expirationTime,
                          next: null,
                          previous: null,
                        };
                    
                        // Insert the new callback into the list, sorted by its expiration. This is
                        // almost the same as the code in `scheduleCallback`, except the callback
                        // is inserted into the list *before* callbacks of equal expiration instead
                        // of after.
                        if (firstCallbackNode === null) {
                          // This is the first callback in the list.
                          firstCallbackNode = continuationNode.next = continuationNode.previous = continuationNode;
                        } else {
                          var nextAfterContinuation = null;
                          var node = firstCallbackNode;
                          do {
                            if (node.expirationTime >= expirationTime) {
                              // This callback expires at or after the continuation. We will insert
                              // the continuation *before* this callback.
                              nextAfterContinuation = node;
                              break;
                            }
                            node = node.next;
                          } while (node !== firstCallbackNode);
                    
                          if (nextAfterContinuation === null) {
                            // No equal or lower priority callback was found, which means the new
                            // callback is the lowest priority callback in the list.
                            nextAfterContinuation = firstCallbackNode;
                          } else if (nextAfterContinuation === firstCallbackNode) {
                            // The new callback is the highest priority callback in the list.
                            firstCallbackNode = continuationNode;
                            ensureHostCallbackIsScheduled();
                          }
                    
                          var previous = nextAfterContinuation.previous;
                          previous.next = nextAfterContinuation.previous = continuationNode;
                          continuationNode.next = nextAfterContinuation;
                          continuationNode.previous = previous;
                        }
                      }
                    }
                    
                • 在finally 中执行了 ensureHostCallbackIsScheduled
                  • 在这个方法中,有个判断是 isHostCallbackScheduled 仅且仅有 在这个判断中,isHostCallbackScheduled 才会被设置为 true
                  • 所以,在有 firstCallbackNode 时,调用 ensureHostCallbackIsScheduled() 时,isHostCallbackScheduled 是 true 的
                  • 它为 true 时,在 ensureHostCallbackIsScheduled() 中,会执行 cancelHostCallback()
                    cancelHostCallback = function() {
                      scheduledHostCallback = null;
                      isMessageEventScheduled = false;
                      timeoutTime = -1;
                    };
                    
                  • 也就是说把之前调度的变量都重置了,不能让老的callback再执行一遍,以此可能导致产生错误
                  • 在 finally 中的 else 环节 isHostCallbackScheduled 被设置成 false
                • 最后执行了 flushImmediateWork 这个API 以后可能会用到
                  function flushImmediateWork() {
                    // 这里 ImmediatePriority 是一个固定的值 但是 firstCallbackNode.priorityLevel
                    // 这里 firstCallbackNode.priorityLevel 是 固定的 3 这个if 不会被执行
                    // 所以,这个api 暂未开放
                    if (
                      // Confirm we've exited the outer most event handler
                      currentEventStartTime === -1 &&
                      firstCallbackNode !== null &&
                      firstCallbackNode.priorityLevel === ImmediatePriority
                    ) {
                      isExecutingCallback = true;
                      deadlineObject.didTimeout = true;
                      try {
                        do {
                          flushFirstCallback();
                        } while (
                          // Keep flushing until there are no more immediate callbacks
                          firstCallbackNode !== null &&
                          firstCallbackNode.priorityLevel === ImmediatePriority
                        );
                      } finally {
                        isExecutingCallback = false;
                        if (firstCallbackNode !== null) {
                          // There's still work remaining. Request another callback.
                          ensureHostCallbackIsScheduled();
                        } else {
                          isHostCallbackScheduled = false;
                        }
                      }
                    }
                  }
                  

总结

  • 以上是 react scheduler 模拟 requestIdleCallback 时间片的操作和调度
  • 能够控制把更多的优先权交给浏览器,让它去做动画或用户输入反馈的更新
  • 在有空闲的时间,回过头来执行 react 的异步更新操作
  • 在这里面会有各种各样的计时来控制帧时间的判断
  • 如果发现浏览器的刷新频率更高,则调低帧时间,以及判断任务是否有过期
  • 如果过期了,需要强制输出
  • 从这个React版本来看,react scheduler 只开放了一部分代码,还有一部分代码暂时没有用到
pytorch实现手语识别 连续手语识别项目,中文数据集CSL 阅读详情

相关推荐

告别迷茫!手把手教你用Altova XMLSpy编写EtherCAT从站ESI文件(附避坑指南)

本文详细介绍了如何使用Altova XMLSpy高效编写EtherCAT从站ESI文件,涵盖核心架构、通信通道配置、过程数据映射等关键步骤,并提供了常见问题排查与性能优化建议。通过实战案例和避坑指南,帮助工程师快速掌握EtherCAT从站开发技巧,提升工作效率。

weixin_42570484的博客 396

React18源码: schedule任务调度messageChannel

看完这8个内部函数,最后浏览一下完整的 performWorkUntilDeadline 回调的实现。调度中心最核心的代码,在 SchedulerHostConfig.default.js 中。这4个函数代码都很简洁,其功能在注释中都有解释.1 )调度相关:请求或取消调度。

Wang的专栏 1843

S/4中的16种特殊库存类型

S/4中的16种特殊库存类型

yanzibing99的博客 1万+

深入分析React-Scheduler原理

关键词:react react-scheduler scheduler 时间切片 任务调度 workLoop。

xiaofeng123aazz的博客 259

Win11系统下Abaqus2022与VS2022+oneAPI2023环境配置全攻略(附VUMAT子程序避坑指南)

本文详细指导在Windows 11系统下,为Abaqus 2022配置Visual Studio 2022与Intel oneAPI 2023的Fortran编译环境,以支持VUMAT等用户子程序开发。内容涵盖软件安装顺序、环境变量关联、快捷方式修改及关键验证步骤,并提供了首个VUMAT子程序的实战编写与模型验证方法,帮助用户高效搭建稳定可靠的仿真开发平台。

pytorch8learner的博客 319

昇腾910安装驱动出错,降低Centos7.6的内核版本

Centos7.6 启动内核修改,解决昇腾910安装驱动错误的问题。

深蓝 3302

npm安装时提示错误checkPermissions Missing write access to /Users/xxx/node_modules/scheduler

权限问题 当我们使用npm install安装的时候,出现错误提示"checkPermissions Missing write access to /Users/xxx/node_modules/scheduler",这是由于权限的问题,导致了安装失败。 解决办法 办法1:尝试使用sudo npm install,但有些情况下无效。 办法2: 首先删除原先的node_modules文件夹 执行npm install 把node_modules文件夹的权限修改为777 $ sudo chown -R 7

Panda0218的博客 1032

《AgentScope-Java 深入浅出教程》第2章 环境搭建与第一个智能体

创建// 【导入说明】/*** 时间相关工具类* 工具类是普通的 Java 类,通过注解声明为工具。* 智能体可以根据用户需求自动调用这些工具。*/// 【工具定义】获取当前时间@Tool(name = "get_current_time", // 工具名称:LLM 看到的名称description = "获取指定时区的当前时间" // 工具描述:帮助 LLM 理解何时使用// 【参数定义】// @ToolParam 的 name 属性是必需的!

步子哥的博客 418

【Oracle】SCHEDULER使用详解

SCHEDULER使用详解:   创建SCHEDULER: BEGIN sys.dbms_scheduler.create_schedule( repeat_interval => 'FREQ=DAILY;BYHOUR=15;BYMINUTE=0;BYSECOND=0', start_date => systimestamp at time zone '+8:00', end_dat

badly9的专栏 3938

YOLOv11改进策略【SPPF】| AIFI : 基于Transformer的尺度内特征交互,在降低计算成本的同时提高模型的性能

本文记录的是基于AIFI模块的YOLOv11目标检测改进方法研究。AIFI是RT-DETR中高效混合编码器的一部分,利用其改进YOLOv11模型,

Limiiiing的博客 2241

Camunda 7.x 系列【62】启动流程实例源码解析

为了更好的了解Camunda,接下来我们简单分析下启动流程实例相关源码

记录知识、锤炼自我 1175

React scheduler 源码分析(16.8版本)

优化背景: 在react16 版本之前,react 的更新渲染存在着一些性能的问题,这个问题产生的原因是因为在更新/渲染时,采用递归的方式(栈调度器),而递归有一个不好的地方就是,他不能被随意的break;也不能在break之后也很难恢复到break之前的状态,而因为浏览器是单线程的,V8 引擎执行js 和 渲染引擎渲染页面是两个互斥的行为,这也就是说当更新任务太庞大的时候,js执行太久而致使页面丢帧,不流畅,甚至卡顿。 优化方式 react16 之后,react团队在react中加入了一个全新的架构:Fi

jurongchao的博客 699

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【React源码11】深入学习React 源码实现——调度器(Scheduler)底层实现

React调度器(Scheduler)是React实现并发模式的核心机制,它通过优先级调度和时间切片技术优化渲染性能。本文详细解析了Scheduler的设计原理

silverclayz的技术分享 1129

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

React16源码: React中调度之requestWork的源码实现

在 scheduleWork 中,找到了创建更新的fiber对应的root节点, 然后对它进行了一些操作之后,调用了 `requestWork`,开始请求工作, 在 requestWork 里面它会做哪些东西呢?首先我们要把这个root节点加入到调度队列当中, 然后判断是否是批量更新, 最后再根据 expirationTime 的类型来判断调度的类型

Wang的专栏 1144

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

异步调度 scheduler 原理

前端异步任务调度

Jacoh的专栏 758

【React源码15】深入学习React 源码实现——如何实现多线程调度(Web Worker + Scheduler)

React通过结合Web Worker和调度器(Scheduler)实现多线程任务处理,将计算密集型操作移出主线程以提升性能。核心架构分为主线程和Worker线程:主线程负责UI渲染和用户交互,通过postMessage将任务发送给Worker处理;Worker线程完成计算后返回结果。实现时需创建Worker文件处理任务,封装调度器接口管理任务优先级,并在React组件中调用。这种方案有效解决了JavaScript单线程瓶颈,特别适用于大数据计算等场景,保持UI响应性同时提升整体性能。

silverclayz的技术分享 1108

react源码学习第 5 周:调度系统(Scheduler)

理解 React 是怎么实现异步渲染掌握优先级调度的核心机制。

1142

React源码分析(一)=> scheduler分析

`scheduler`这个包主要是在react做diff做任务分配机制, 核心机制类似于[requestidlecallback](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback), >`window.requestIdleCallback()`会在浏览器空闲时期依次调用函数, 这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这样延迟敏感的事件产生影响。

翾的博客 1811

React 源码文件结构解析:从 Scheduler-Reconciler-Renderer 架构到 packages 目录

本文是「React技术揭秘」源码分析系列中的关键一环:在了解了 React 16 的 **Scheduler(调度器)— Reconciler(协调器)— Renderer(渲染器)** 三层架构之后,我们需要知道这套架构是如何落到源码的文件结构上的。读完本文,你将掌握 `react` 仓库根目录与 `packages` 目录的完整组织方式,能够准确区分核心包、Renderer 包、试验性包与辅助

gitblog_00438的博客 188

React 技术揭秘:React16 的三层架构(Scheduler-Reconciler-Renderer)如何支撑异步可中断更新

> 本文基于「React 技术揭秘」仓库 [docs/preparation/newConstructure.md](https://link.gitcode.com/i/2f46e28e79a56eaedddcb9e1b671ad21) 展开,并融合仓库内 [docs/preparation/oldConstructure.md](https://link.gitcode.com/i/52895

gitblog_00842的博客 366

react中settimeout_React 源码Scheduler(三)React的调度算法实现

本文源码基于 React 16.8.6 (March 27, 2019),仅记录一些个人阅读源码的分享与体会。欢迎大家交流和探讨React 源码 Scheduler(一)浏览器的调度React 源码 Scheduler (二) React 的调度流程React 源码 Scheduler(三)React的调度算法实现前言在上两节中,笔者介绍了在浏览器中存在的 requestAnimationFram...

weixin_39751195的博客 150

react强制刷新页面_React 源码Scheduler(三)React的调度算法实现

本文源码基于 React 16.8.6 (March 27, 2019),仅记录一些个人阅读源码的分享与体会。欢迎大家交流和探讨React 源码 Scheduler(一)浏览器的调度React 源码 Scheduler (二) React 的调度流程React 源码 Scheduler(三)React的调度算法实现前言在上两节中,笔者介绍了在浏览器中存在的 requestAnimationFram...

weixin_39890543的博客 290

just-react 并发模式指南:从入口函数到 Fiber 架构、Scheduler 与 lane 模型,理解 React Concurrent Mode 的底层实现

本文基于「React技术揭秘」仓库中 [docs/concurrent/prepare.md](https://link.gitcode.com/i/33780b2be1b49e99396ad961b1cd2c07) 一文展开。该文档是全书第八章《Concurrent Mode》的开篇总览,回答了三个核心问题:Concurrent 模式是什么、它为什么值得 React 花两年时间重构 Fiber

gitblog_01103的博客 890

【React架构 - Scheduler中的MessageChannel】

【React架构 - Scheduler中的MessageChannel】

weixin_44273311的博客 2085

react源码解析16.concurrent模式

react源码解析16.concurrent模式 视频讲解(高效学习):进入学习 往期文章: 1.开篇介绍和面试题 2.react的设计理念 3.react源码架构 4.源码目录结构和调试 5.jsx&核心api 6.legacy和concurrent模式入口函数 7.Fiber架构 8.render阶段 9.diff算法 10.commit阶段 11.生命周期 12.状态更新流程 13.hooks源码 14.手写hooks 15.scheduler&Lane 16.concurrent模式

zz1999aa的博客 123

React Suspense实现原理:just-react异步渲染机制深度解析

React Suspense作为React并发模式的核心特性,彻底改变了我们处理异步数据加载的方式。通过just-react项目的源码分析,本文将深入揭示Suspense的异步渲染机制和工作原理。 ## 🤔 什么是React Suspense? React Suspense是React 16.6引入的一项革命性功能,它允许组件在等待异步数据时"暂停"渲染,显示fallback内容,待数据就绪

gitblog_00334的博客 886
上一篇: React16源码: React中调度之batchedUpdates的源码实现
下一篇: React16源码: React中的performWork的源码实现
Wang's Blog
博客等级 码龄12年 7434粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值