修复Safari中的顶层Await

修复Safari中的顶层Await

💡 原文英文,约1600词,阅读约需6分钟。
📝

内容提要

Safari 27修复了顶层await的兼容性问题,重写了模块加载器,从自托管JavaScript改为原生C++实现,解决了模块执行顺序错误和“访问前初始化”报错。新加载器通过测试验证,支持顶层await和ES模块,开发者可在生产环境中使用。

🔎

延伸解读

为何旧版Safari会出现“访问前初始化”错误

旧版Safari的模块加载器基于已废弃的WHATWG Loader提案,该提案早于ES2022的顶层await。当模块执行到顶层await时,加载器会错误地提前解析后续导入的Promise,导致依赖模块尚未完成求值就被访问,从而抛出“Cannot access before initialization”错误。此问题源于加载器对异步模块执行顺序的错误处理,而非开发者代码本身。

从自托管JavaScript到原生C++的迁移

WebKit团队将模块加载器从自托管JavaScript重写为原生C++实现。自托管JavaScript虽可内联优化,但启动时需编译,且模块加载并非热路径,导致性能不稳定。原生C++代码预编译,启动更快,性能更可预测。这一改动不仅修复了顶层await,也为ES模块整体提供了更坚实的基础。

测试策略:模糊测试与跨引擎对比

为确保新加载器正确性,WebKit团队编写了模糊测试器,生成包含顶层await的复杂模块图,并将JavaScriptCore的输出与其他引擎逐字节对比。同时,Bun工程师提供了历史问题测试用例。所有test262模块测试和WPT测试均通过,且无回归,新加载器才被合并。

Q&A

Safari 27 修复了关于顶层 await 的什么问题?

Safari 27 修复了顶层 await 的兼容性问题,解决了模块执行顺序错误和“访问前初始化”报错。

什么是顶层 await?

顶层 await 允许在模块的顶层使用 await 关键字,使得模块可以像 async 函数一样方便地使用 Promise。当模块遇到顶层 await 时,会暂停执行直到 await 的值可用,同时导入该模块的其他模块也会被挂起,但不依赖该模块的兄弟模块仍可并发执行。

旧版 Safari 的模块加载器为什么会导致顶层 await 出错?

旧版模块加载器基于已废弃的 WHATWG Loader 提案,该提案早于 async/await 的出现,无法正确处理异步模块执行。这导致模块加载顺序错误,并引发“访问前初始化”错误。

Safari 如何修复模块加载器?

WebKit 团队重写了模块加载器,从自托管的 JavaScript 实现改为原生 C++ 实现,并严格按照 ECMAScript 规范中的算法进行翻译。

自托管 JavaScript 实现模块加载器有哪些缺点?

自托管 JavaScript 需要运行时编译,启动较慢;由于内置函数使用广泛,优化 JIT 难以利用使用模式;且模块加载器不是热路径,运行时编译收益低,导致性能不稳定。

如何测试新的模块加载器?

测试包括:采用 Bun 提供的测试用例,编写模糊测试生成复杂模块图,并与其他 JavaScript 引擎的输出进行字节级比较,同时确保 test262 和 WPT 的模块相关测试全部通过。

开发者何时可以使用 Safari 27 的顶层 await?

开发者现在可以下载 Safari Technology Preview 251 或 Safari 27 beta 进行尝试,Safari 27 正式发布后即可在生产环境中使用。

🏷️

标签

➡️

继续阅读