本文介绍了两个 JavaScript 提案:Import Sync 提案和 Defer Import Eval 提案。Import Sync 提案建议引入一个同步导入函数 import.sync,用于在模块已加载的情况下同步导入模块。Defer Import Eval 提案建议引入一种新的同步导入形式 import defer,以避免在应用初始化过程中不必要的 CPU 工作,仅在访问模块属性时触发执行。
.常见使用案例包括获取已加载的模块、条件加载和同步加载模块表达式和声明。
.使用 import defer 导入的模块,其异步依赖会立即执行,而同步部分会被推迟执行。
.通过同步导入和延迟执行,可以提高大型应用程序的性能,避免阻塞主事件循环。
.提供了一个粗略的模块包装器实现示例,用于实现类似 Defer Import Eval 的行为,通过异步加载模块及其依赖项,并在访问属性时同步执行。
// 如果模块可同步获取,则同步导入模块 const ns = import.sync('./mod.js');当模块不可同步获取或使用顶层 await 时,该函数会抛出一个新错误。模块是否可同步获取则是由主机决定的属性。
import 'app'; // 堆代码 duidaima.com // 如果 'app' 模块已经加载,则这将始终工作 const app = import.sync('app');条件加载
let fs; try { fs = import.sync('node:fs'); } catch {} if (fs) { // 只有当 fs 模块可用时,才使用 node:fs }模块表达式和声明的同步加载
// 立即日志输出 'hello world' import.sync(module { console.log('hello world'); })同样适用于模块声明:
module dep { console.log('hi'); } module x { import dep; } // 如果两个模块都是同步可用的,日志输出 'hi' const instance = import.sync(x);Defer Import Eval 提案(Stage 2.7)
// 优化前 const operation = require('operation'); exports.doSomething = function (target) { return operation(target); } // 优化后 exports.doSomething = function (target) { const operation = require('operation'); return operation(target); }对于 ES 模块,可以通过动态导入来实现懒加载:
export async function doSomething (target) { const { operation } = await import('operations'); return operation(target); }然而,上面方法并没有解决性能瓶颈问题,尤其是初始化时的 CPU 占用。动态导入常需预加载步骤,并迫使所有函数及其调用者进入异步模型,这与程序的真实意图是不符合的,而且会导致 API 的更改。本提案建议引入一个新的同步导入形式,通过该形式可避免在应用初始化过程中不必要的 CPU 工作,且无需改变模块 API 消费者的使用方式。当访问模块的属性时,仅在必要时触发同步评估。
// 示例语法 import defer * as yNamespace from "y";这种导入方式会参与深层图加载,将模块及其依赖项加载至准备执行的状态,但不会立刻执行,只有在访问其属性时才触发执行。
const operation = import.defer('./operation'); function executeOperation(target) { return operation.default(target); }我们还可以用它来实现同步检查模块或内置模块是否可用:
let fs; try { fs = import.defer('node:fs'); } catch {} if (fs) { // 使用 node:fs,仅在其可用时 }同步属性访问必须同步进行,因此不可能将使用顶级 await 的模块推迟执行。使用 import defer 语法导入的模块,其异步依赖和传递依赖将会被立即执行,而只有同步部分会被推迟。可能大家会有个问题,为什么需优化执行?加载不是瓶颈吗?
// LazyModuleLoader.js async function loadModuleAndDependencies(name) { const loadedModule = await import.load(`./${name}.js`); // 加载模块,需等待异步完成 const parsedModule = loadedModule.parse(); await Promise.all(parsedModule.imports.map(loadModuleAndDependencies)); // 加载所有依赖 return parsedModule; } async function executeAsyncSubgraphs(module) { if (module.hasTLA) return module.evaluate(); return Promise.all(module.importedModules.map(executeAsyncSubgraphs)); } export default async function lazyModule(object, name) { const module = await loadModuleAndDependencies(name); await executeAsyncSubgraphs(module); Object.defineProperty(object, name, { get: function() { delete object[name]; const value = module.evaluateSync(); Object.defineProperty(object, name, { value, writable: true, configurable: true, enumerable: true, }); return value; }, configurable: true, enumerable: true, }); return object; }最后