深入剖析vscode工具函数(五)lazy模块
什么是Lazy
Lazy 是一种计算机程序设计技术,用于实现延迟加载。它的核心思想是:只有在需要的时候才计算和加载数据,而不是在程序启动时或者数据传输时提前加载和计算数据。这样可以避免不必要的计算和内存占用,提高程序的效率和性能。
在计算机科学中,延迟加载是一种非常重要的技术,因为它可以帮助程序避免不必要的计算和内存占用,提高程序的效率和性能。在实际开发中,延迟加载被广泛应用于各种类型的软件和系统中,比如网络应用、游戏开发、数据处理和函数式编程等领域。
在 JavaScript 中,使用 Lazy 技术可以实现延迟加载,避免不必要的计算和内存占用,提高程序的效率和性能。Lodash 库中的 LazyWrapper 类就是基于这种惰性求值的思想而开发的,它可以用来实现链式调用的惰性执行,并提供了一些方便的方法和函数,可以帮助开发者实现延迟加载。
Lazy的主要作用
Lazy 主要的作用是实现延迟加载,即将某些操作推迟到必要的时候再执行。这个概念在计算机科学中非常重要,因为它可以帮助程序减少不必要的计算和内存占用,提高程序的效率和性能。以下是 Lazy 常见的应用场景和作用:
- 提高程序性能:当某些计算比较复杂或者需要大量的内存和计算资源时,使用
Lazy可以将计算延迟到必要的时候再执行,避免不必要的计算和内存占用,提高程序的性能。 - 延迟加载数据:在网络应用中,当需要加载大量数据或者文件时,使用
Lazy可以将数据或文件的加载推迟到必要的时候再执行,避免不必要的网络传输和带宽占用,提高用户体验。 - 惰性求值:在函数式编程中,使用
Lazy可以实现惰性求值,即只有在需要时才计算和求值,避免不必要的计算和内存占用,提高程序的效率和性能。 - 链式调用:在一些场景中,需要进行多次操作,并将这些操作组成一个链式调用。使用
Lazy可以实现链式调用的惰性执行,避免不必要的计算和内存占用,提高程序的效率和性能。
VSCode的Lazy实现
export class Lazy<T> {
private _didRun: boolean = false;
private _value?: T;
private _error: Error | undefined;
constructor(
private readonly executor: () => T,
) { }
/**
* True if the lazy value has been resolved.
*/
get hasValue() { return this._didRun; }
/**
* Get the wrapped value.
*
* This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only
* resolved once. `getValue` will re-throw exceptions that are hit while resolving the value
*/
get value(): T {
if (!this._didRun) {
try {
this._value = this.executor();
} catch (err) {
this._error = err;
} finally {
this._didRun = true;
}
}
if (this._error) {
throw this._error;
}
return this._value!;
}
/**
* Get the wrapped value without forcing evaluation.
*/
get rawValue(): T | undefined { return this._value; }
}
这个实现并不复杂,主要说明一下:
Lazy类是一个泛型类,可以适用于各种类型的数据和对象。Lazy类有一个私有变量_didRun,用于记录是否已经执行过计算。Lazy类有一个私有变量_value,用于缓存计算结果。Lazy类有一个私有变量_error,用于记录计算过程中可能发生的异常和错误。Lazy类有一个构造函数,接收一个参数executor,它是一个函数类型,用于执行实际的计算操作。Lazy类有一个只读属性hasValue,用于判断是否已经执行过计算。Lazy类有一个属性value,用于获取计算结果,如果计算结果还没有被缓存,就调用executor函数进行计算,并将计算结果缓存到_value变量中。如果计算过程中发生了异常,就将异常信息缓存到_error变量中,并通过throw抛出异常。value属性只能调用一次,后续调用将直接返回缓存的计算结果。Lazy类有一个属性rawValue,用于获取计算结果,但不会执行实际的计算操作。如果计算结果还没有被缓存,就返回undefined,否则返回已经缓存的计算结果。
Lazy的使用方式
VSCode中存在大量对 Lazy 的使用,在这里给出一个通用的使用方式:
// 定义一个需要延迟加载的函数
function expensiveCalculation(): number {
console.log('Executing expensiveCalculation');
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result;
}
// 创建一个 Lazy 对象
const lazyValue = new Lazy(() => expensiveCalculation());
// 访问 Lazy 对象的值
console.log(lazyValue.hasValue); // false
const value1 = lazyValue.value; // 这里会触发 expensiveCalculation 的执行
console.log(value1); // 输出 expensiveCalculation 的返回值
console.log(lazyValue.hasValue); // true
const value2 = lazyValue.value; // 这里不会再次执行 expensiveCalculation
console.log(value2); // 输出 expensiveCalculation 的返回值
const rawValue = lazyValue.rawValue; // 可以直接访问缓存的计算结果
console.log(rawValue); // 输出 expensiveCalculation 的返回值
实际上 Lazy 的实现包含了 once 的能力,对于使用 Lazy 来执行的函数来说,它只会被执行一次,并且是访问 .value 的使用再执行。
这里有了 value 为什么还需要一个 rawValue 呢?仔细看实现就会发现,**value**属性会在需要时执行传入的函数并保存执行结果,而 **rawValue**属性则不会执行传入的函数,只是返回保存的结果。有些情况下,我们是不需要函数被执行的,比如一些判断的场景。
以下是VSCode中使用 rawValue 的场景:
this._register(this.model.onChangedHints(newParameterHints => {
if (newParameterHints) {
this.widget.value.show();
this.widget.value.render(newParameterHints);
} else {
this.widget.rawValue?.hide();
}
}));
当我们调用 hide 的时候,并不知道 widget 是否已经执行过了,而我们并不想带来副作用,所以这个时候不是取 value ,而是 rawValue 。
小结
本文探讨了Lazy的概念,它是一种编程技术,用于实现延迟加载。延迟加载的核心思想是在需要时计算和加载数据,而不是在程序启动或数据传输期间提前加载和计算数据。这种方法可以帮助减少不必要的计算和内存使用,提高程序的效率和性能。
延迟加载是计算机科学中的一项关键技术,并广泛应用于各种类型的软件和系统,例如网络应用程序、游戏开发、数据处理和函数式编程。在JavaScript中,使用延迟加载可以避免不必要的计算和内存使用,提高程序的效率和性能。
在VSCode中,实现惰性的方式并不复杂,但应用非常多。在普通的Web开发中,可能我们并不太关注计算性能及内存带来的消耗,但是在VSCode这样大型桌面应用中,需要非常精细地控制计算性能和内存的开销,性能的保证提现在细节习惯中。在写代码时,一定要时刻问自己,是不是需要立即执行?是不是可以延迟加载?保持心中的准绳。