11---
22layout : post
3- title : " VSCode: Centralized Animation Frame Scheduling"
3+ title : " VSCode: Centralized Animation Frame Scheduling"
44categories : [VSCode, UI]
55---
66
7- ## 简介
8- 今天介绍一个VSCode源码中一个简单且小巧的功能。在此之前我先简单介绍一下 ` requestAnimationFrame ` 这个原生API。
7+ ## Introduction
8+ Today, we'll explore a simple and elegant feature within the VSCode source code. Before diving in, let’s first look at the native ` requestAnimationFrame ` API.
99
1010## ` requestAnimationFrame ` API
1111
12- ### 工作原理
13- ` requestAnimationFrame ` 会将提供的回调函数放入队列,并在下一次浏览器重绘前调用该函数。与传统的 ` setTimeout ` 不同, ` requestAnimationFrame ` 的优势在于:
14- - ** 与屏幕刷新同步 ** :浏览器会在适当的时间调用回调函数,通常是每秒 60 帧(即 16.67ms 间隔)。
15- - ** 节能效果 ** :当页面处于后台或不可见状态时,浏览器会暂停 ` requestAnimationFrame ` 的调用,从而节省资源。
16- - ** 平滑的动画 ** :由于与浏览器刷新周期一致,动画会显得更加流畅。
12+ ### How It Works
13+ ` requestAnimationFrame ` queues a callback to be invoked before the next browser repaint. Compared to ` setTimeout ` , it offers several advantages:
14+ - ** Synchronization with screen refresh ** : The callback is invoked at an appropriate time, typically 60 frames per second ( 16.67ms interval).
15+ - ** Energy efficiency ** : When the page is in the background or hidden, the browser halts ` requestAnimationFrame ` calls to conserve resources.
16+ - ** Smooth animations ** : Animations appear smoother due to synchronization with the browser’s refresh cycle.
1717
18- ### 基本用法
18+ ### Basic Usage
1919``` javascript
2020function animate () {
2121 console .log (' Animating...' );
2222}
2323requestAnimationFrame (animate);
2424```
25- ### 问题点
26- 当应用中的多个模块独立调用 ` requestAnimationFrame ` 时,可能出现以下问题:
27- 1 . ** 缺乏全局优先级控制** :浏览器无法直接区分任务的优先级,导致关键任务与次要任务并行执行,影响性能。
28- 2 . ** 复杂任务调度** :对于需要动态更新任务优先级或取消任务的场景,原生 API 支持不足。
2925
30- 为了解决这些问题,VSCode 设计了一套中央式动画帧调度器来管理动画和 UI 渲染任务。
26+ ### Issues
27+ When multiple modules independently call ` requestAnimationFrame ` , issues can arise:
28+ 1 . ** Lack of global priority control** : The browser cannot distinguish between critical and minor tasks, leading to potential performance bottlenecks.
29+ 2 . ** Complex task scheduling** : The native API offers limited support for dynamically updating priorities or canceling tasks.
3130
32- ## 中央调度器
33- > 相关文件:` src\vs\base\browser\dom.ts `
31+ To address these challenges, VSCode employs a centralized animation frame scheduler for managing animation and UI rendering tasks.
3432
35- ` VSCode ` 整个软件中避免直接使用` window.requestsAnimationFrame ` ,而是提供了以下两个APIs:
36- ``` ts
33+ ## Centralized Scheduler
34+ > Relevant File: ` src\vs\base\browser\dom.ts `
35+
36+ VSCode avoids directly using ` window.requestAnimationFrame ` . Instead, it provides the following two APIs:
37+ ``` typescript
3738/**
3839 * Schedule a callback to be run at the next animation frame.
39- * This allows multiple parties to register callbacks that should run at the next animation frame.
40- * If currently in an animation frame, `runner` will be executed immediately.
41- * @return token that can be used to cancel the scheduled runner (only if `runner` was not executed immediately).
40+ * Allows multiple parties to register callbacks to run at the next animation frame.
41+ * If currently in an animation frame, `runner` executes immediately.
42+ * @return token to cancel the scheduled runner (only if `runner` was not executed immediately).
4243 */
4344export let runAtThisOrScheduleAtNextAnimationFrame: (targetWindow : Window , runner : () => void , priority ? : number ) => IDisposable ;
45+
4446/**
4547 * Schedule a callback to be run at the next animation frame.
46- * This allows multiple parties to register callbacks that should run at the next animation frame.
47- * If currently in an animation frame, `runner` will be executed at the next animation frame.
48- * @return token that can be used to cancel the scheduled runner.
48+ * Allows multiple parties to register callbacks to run at the next animation frame.
49+ * If currently in an animation frame, `runner` executes at the next animation frame.
50+ * @return token to cancel the scheduled runner.
4951 */
5052export let scheduleAtNextAnimationFrame: (targetWindow : Window , runner : () => void , priority ? : number ) => IDisposable ;
5153```
5254
53- VSCode把函数的定义写在了一个 immediate call function 里面。因为代码量很少,我会直接把大部分代码复制过来。首先是在 function body 中定义了一些map,用来全局储存数据:
54- ``` ts
55- (function () {
56- // The runners scheduled at the next animation frame
57- const NEXT_QUEUE = new Map < number /* window ID */ , AnimationFrameQueueItem[]>();
58- // The runners scheduled at the current animation frame
59- const CURRENT_QUEUE = new Map < number /* window ID */ , AnimationFrameQueueItem[]>();
60- // A flag to keep track if the native requestAnimationFrame was already called
61- const animFrameRequested = new Map < number /* window ID */ , boolean>();
62- // A flag to indicate if currently handling a native requestAnimationFrame callback
63- const inAnimationFrameRunner = new Map < number /* window ID */ , boolean>();
55+ These functions are defined within an immediately invoked function expression (IIFE) for encapsulation. Here’s a simplified breakdown of the code:
6456
57+ ### Data Structures
58+ Maps are used to store global data:
59+ ``` typescript
60+ (function () {
61+ const NEXT_QUEUE = new Map <number , AnimationFrameQueueItem []>();
62+ const CURRENT_QUEUE = new Map <number , AnimationFrameQueueItem []>();
63+ const animFrameRequested = new Map <number , boolean >();
64+ const inAnimationFrameRunner = new Map <number , boolean >();
6565 // ...
6666})();
6767```
6868
69- 而` runAtThisOrScheduleAtNextAnimationFrame ` 和` scheduleAtNextAnimationFrame ` 的定义如下:
70- ``` ts
69+ ### API Implementations
70+ #### ` scheduleAtNextAnimationFrame `
71+ Schedules tasks for the next animation frame:
72+ ``` typescript
7173(function () {
7274 // ...
7375 scheduleAtNextAnimationFrame = (targetWindow : Window , runner : () => void , priority : number = 0 ) => {
@@ -85,7 +87,14 @@ VSCode把函数的定义写在了一个 immediate call function 里面。因为
8587 }
8688 return item ;
8789 };
90+ })();
91+ ```
8892
93+ #### ` runAtThisOrScheduleAtNextAnimationFrame `
94+ Runs the task immediately if within an animation frame, otherwise schedules it for the next frame:
95+ ``` typescript
96+ (function () {
97+ // ...
8998 runAtThisOrScheduleAtNextAnimationFrame = (targetWindow : Window , runner : () => void , priority ? : number ) => {
9099 const targetWindowId = getWindowId (targetWindow );
91100 if (inAnimationFrameRunner .get (targetWindowId )) {
@@ -103,10 +112,9 @@ VSCode把函数的定义写在了一个 immediate call function 里面。因为
103112 };
104113})();
105114```
106- * ` AnimationFrameQueueItem ` 是任务的基本单位。每个任务都被封装成一个实例,包含了以下信息:
107- * ** 任务逻辑** (` runner ` ):需要执行的具体函数。
108- * ** 优先级** (` priority ` ):用于控制任务执行的顺序。
109- * ** 取消标志** (` _canceled ` ):支持任务的动态取消。
115+
116+ ### Task Representation
117+ Each task is encapsulated in an ` AnimationFrameQueueItem ` :
110118``` typescript
111119class AnimationFrameQueueItem {
112120 private _runner: () => void ;
@@ -133,33 +141,35 @@ class AnimationFrameQueueItem {
133141 }
134142}
135143```
136- 调度的核心逻辑是 ` animationFrameRunner ` 函数,它通过 ` requestAnimationFrame ` 在每帧执行任务:
137- 1 . 从 ` NEXT_QUEUE ` 中提取任务到 ` CURRENT_QUEUE ` 。
138- 2 . 对 ` CURRENT_QUEUE ` 按优先级排序。
139- 3 . 按顺序依次执行任务。
144+
145+ ### Core Scheduling Logic
146+ The ` animationFrameRunner ` function manages task execution:
147+ 1 . Transfers tasks from ` NEXT_QUEUE ` to ` CURRENT_QUEUE ` .
148+ 2 . Sorts tasks in ` CURRENT_QUEUE ` by priority.
149+ 3 . Executes tasks sequentially:
140150``` typescript
141151(function () {
142152 // ...
143153 const animationFrameRunner = (targetWindowId : number ) => {
144- animFrameRequested .set (targetWindowId , false );
154+ animFrameRequested .set (targetWindowId , false );
145155
146- const currentQueue = NEXT_QUEUE .get (targetWindowId ) ?? [];
147- CURRENT_QUEUE .set (targetWindowId , currentQueue );
148- NEXT_QUEUE .set (targetWindowId , []);
156+ const currentQueue = NEXT_QUEUE .get (targetWindowId ) ?? [];
157+ CURRENT_QUEUE .set (targetWindowId , currentQueue );
158+ NEXT_QUEUE .set (targetWindowId , []);
149159
150- while (currentQueue .length > 0 ) {
151- currentQueue .sort (AnimationFrameQueueItem .sort );
152- const top = currentQueue .shift ()! ;
153- top .execute (); // actual animation execution
154- }
155- // ...
156- };
160+ while (currentQueue .length > 0 ) {
161+ currentQueue .sort (AnimationFrameQueueItem .sort );
162+ const top = currentQueue .shift ()! ;
163+ top .execute ();
164+ }
165+ // ...
166+ };
157167})();
158168```
159169
160- ## 额外内容
161- VSCode还提供了一些简单的helper function方便做测试或者补丁等等:
162- ``` ts
170+ ## Additional Utilities
171+ VSCode provides helper functions for early or late scheduling:
172+ ``` typescript
163173export function measure(targetWindow : Window , callback : () => void ): IDisposable {
164174 return scheduleAtNextAnimationFrame (targetWindow , callback , 10000 /* must be early */ );
165175}
0 commit comments