深入解析 p5.js 友好错误系统(FES):从内部机制到贡献指南
【免费下载链接】p5.jsp5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. Looking for p5.js 2.0? http://beta.p5js.org项目地址: https://gitcode.com/GitHub_Trending/p5/p5.js
<output_article>
p5.js Friendly Error System(FES)深度解析:核心函数、调用链与开发贡献指南
p5.js 的友好错误系统(Friendly Error System,简称 FES)是一套内置于库中的错误诊断与提示机制,它以🌸 p5.js says:为前缀,在浏览器原生报错之外补充更易读、可操作的提示信息。本文以 contributor_docs/fes_contribution_guide.md 为核心骨架,结合仓库源码逐层拆解 FES 的架构、核心函数、运行流程与已知局限,帮助读者理解其内部工作原理,并掌握为 p5.js 贡献 FES 代码的方法。
FES 是什么?——p5.js 的"友好报错"体系
在浏览器控制台中运行 p5.js 草图时,你可能会看到以🌸 p5.js says:开头的消息,它们补充甚至替代了浏览器默认的错误提示。这些消息正是由 p5.js 的 Friendly Error System 生成的。
FES 位于源码 src/friendly_errors/ 目录,它汇集了多个负责生成不同类型友好错误消息的函数。这些函数从各种位置收集错误信息,包括:
- 文件加载错误与浏览器自动播放(autoplay)策略导致的错误处理;
- 库内部函数调用时的参数检查;
- p5.js 贡献者实现的其他自定义错误处理逻辑。
FES 生成友好错误的主要入口函数有四个(详见 fes_core.js 的文件头注释):
| 函数 | 作用 |
|---|---|
p5._friendlyError() | 格式化并通过_report()打印输入消息为友好错误 |
p5._validateParameters() | 校验接收到的输入值是否为错误类型或缺少值 |
p5._friendlyFileLoadError() | 引导用户处理与文件加载函数相关的错误 |
p5._friendlyAutoplayError() | 引导用户处理与浏览器自动播放策略相关的错误 |
注:原文档在正文中以文字形式描述了 FES 各文件的功能关系图,该图对应仓库中的
contributor_docs/images/fes.svg,由于本文聚焦文字化的架构讲解,读者可直接在仓库中查看该 SVG 文件了解函数间的连接关系。
FES 各文件的分工如下(路径均为相对仓库根目录):
- src/friendly_errors/fes_core.js:包含
_report()、_friendlyError()、_friendlyAutoplayError(),以及其他用于格式化和测试友好错误的辅助函数; - src/friendly_errors/param_validator.js:包含
_validateParameters()及其他参数校验辅助函数; - src/friendly_errors/browser_errors.js:包含一份浏览器错误列表,这些错误会通过 FES 的全局错误类(
fes.globalErrors)生成友好提示; - src/friendly_errors/stacktrace.js:包含用于解析错误堆栈的代码(从 stacktrace.js 项目借鉴)。
从源码 src/friendly_errors/index.js 可以看到,FES 由四个 addon 组成,通过p5.registerAddon()注册进 p5 实例:
import fesCore from './fes_core'; import validateParams from './param_validator.js'; import sketchVerifier from './sketch_verifier.js'; import fes from './fes'; export default function (p5) { p5.registerAddon(fes); p5.registerAddon(fesCore); p5.registerAddon(validateParams); p5.registerAddon(sketchVerifier); }这意味着 FES 以插件(addon)形式挂载,p5.FES、p5._friendlyError等静态方法只有在 FES 模块加载后才会被赋予真实实现(未加载时 src/core/main.js 中以空函数 stub 兜底)。
_report():所有友好错误的最终出口
描述
_report()是直接向控制台打印错误辅助消息输出的主要函数。所有友好错误消息最终都会经由它输出。
关键设计:如果设置了p5._fesLogger(例如运行测试时),它会替代console.log被使用。这在通过 Mocha 运行单元测试时非常有用——_fesLogger会让_report将错误消息作为字符串传递给 Mocha,并与断言字符串进行比较。
语法
_report(message); _report(message, func); _report(message, func, color);参数
@param {String} message Message to be printed @param {String} [func] Name of function @param {Number|String} [color] CSS color code[func]输入用于在错误消息末尾追加指向 p5.js 参考文档的链接;[color]输入用于设置错误消息的颜色属性,在当前版本的友好错误消息中并未实际使用。
源码实现
在 fes_core.js 中,p5._report的实现如下(为便于阅读,已省略 JSDoc 注释):
p5._report = (message, func) => { // Add a link to the reference docs of func at the end of the message message = mapToReference(message, func); FES.log`${message}`(); };它调用mapToReference()将func转换为对应的 p5.js 参考文档链接(例如arc会得到https://p5js.org/reference/p5/arc),再通过 fes.js 中FES.log模板函数输出。FES.log会默认加上🌸 p5.js says:前缀,并支持多语言翻译(TL.tl)与样式字符串(%c)。
位置
src/friendly_errors/fes_core.js
_friendlyError():通用的友好错误生成入口
描述
_friendlyError()创建并打印一条友好错误消息。任何 p5 函数都可以调用它来提供友好错误提示。它在 fes_core.js 中的实现非常简洁:
p5._friendlyError = function (message, func) { if (p5.disableFriendlyErrors) return; p5._report(message, func); };可以看到它先检查p5.disableFriendlyErrors开关,再委托给_report()。其调用链为:
_friendlyError _report mapToReference FES.log(控制台输出,带 "🌸 p5.js says:" 前缀)_friendlyFileLoadError():文件加载错误专项指引
描述
_friendlyFileLoadError()为文件加载失败提供专项指引。它在以下 p5 函数内部被调用:
- src/image/loading_displaying.js 中的
loadImage() - src/io/files.js 中的
loadFont()、loadTable()、loadJSON()、loadStrings()、loadXML()、loadBytes()
(从源码注释可见,loadBytes()对应错误类型 1、loadTable()对应 2、loadJSON()对应 3、loadStrings()对应 5、loadXML()对应 6;loadImage()对应类型 0 与 8。)
其调用序列如下:
_friendlyFileLoadError _report语法
_friendlyFileLoadError(errorType, filePath);参数
@param {Number} errorType Number of file load error type @param {String} filePath Path to file caused the errorerrorType对应文件加载错误的特定类型,其枚举定义在core/friendly_errors/file_errors.js(仓库中对应 src/friendly_errors/ 目录)。p5.js 将文件加载错误划分为多种不同场景,以便针对不同错误情况给出精确、信息量充足的提示。例如:当字体文件中的数据无法读取时,与尝试加载过大文件时,会显示不同的错误。
示例
文件加载错误示例——缺失字体文件:
/// missing font file let myFont; function preload() { myFont = loadFont('assets/OpenSans-Regular.ttf'); } function setup() { fill('#ED225D'); textFont(myFont); textSize(36); text('p5*js', 10, 50); } function draw() {}FES 会在控制台生成以下消息(叠加在浏览器原生 "unsupported" 错误之上):
🌸 p5.js says: It looks like there was a problem loading your font. Try checking if the file path (assets/OpenSans-Regular.ttf) is correct, hosting the file online, or running a local server. + More info: https://github.com/processing/p5.js/wiki/Local-server位置
src/friendly_errors/fes_core.js(_friendlyFileLoadError相关实现所在目录为src/friendly_errors/)
_friendlyAutoplayError():浏览器自动播放策略提示
描述
_friendlyAutoplayError()在播放媒体(例如视频)出错时被内部调用,这种错误通常源于浏览器的自动播放策略。
它调用translator()使用键fes.autoplay生成并打印友好错误消息。所有可用的翻译键都可以在 translations/en/translation.json 中查看。
位置
src/friendly_errors/fes_core.js
_validateParameters():参数校验的核心引擎
描述
_validateParameters()通过将输入参数与函数内联文档生成的信息进行匹配来运行参数校验——它检查函数调用是否包含正确数量的参数以及正确类型的参数。
它调用translator()使用键fes.friendlyParamError.*生成并打印友好错误消息(翻译键参见 translations/en/translation.json)。
该函数可通过以下两种方式调用:
p5._validateParameters(FUNCT_NAME, ARGUMENTS); p5.prototype._validateParameters(FUNCT_NAME, ARGUMENTS);推荐在一般情况下使用静态版本p5._validateParameters。p5.prototype._validateParameters(FUNCT_NAME, ARGUMENTS)主要保留用于调试和单元测试。
在仓库中,_validateParameters的实现位于 src/friendly_errors/param_validator.js,它内部维护了一张基于 Zod schema 的schemaMap(含Any、Array、Boolean、Function、Integer、Number、Object、String等基础类型),并通过docs/parameterData.json(即 docs/parameterData.json)中的参数元数据与运行时常量表(constantsMap,来自 src/core/constants.js)来匹配用户实参。
_validateParameters()被内置于以下函数模块中(列表依据原文档并结合仓库结构整理):
- src/accessibility/outputs.js(accessibility/outputs)
- src/color/creating_reading.js(color/creating_reading)
- src/color/setting.js(color/setting)
- src/core/environment.js、src/core/rendering.js、src/core/transform.js(core/environment、core/rendering、core/transform)
- src/shape/2d_primitives.js、src/shape/attributes.js、src/shape/curves.js、src/shape/vertex.js(core/shape/2d_primitives、attributes、curves、vertex)
- src/data/ 中的
p5.TypedDict - src/dom/dom.js(dom/dom)
- src/events/acceleration.js、src/events/keyboard.js(events/acceleration、events/keyboard)
- src/image/image.js、src/image/loading_displaying.js、src/image/p5.Image.js、src/image/pixels.js(image/image、loading_displaying、p5.Image、pixel)
- src/io/files.js(io/files)
- src/math/calculation.js、src/math/random.js(math/calculation、math/random)
- typography/attributes、typography/loading_displaying(对应 src/type/ 相关文件)
- src/utilities/ 中的 string_functions
- src/webgl/3d_primitives.js、src/webgl/interaction.js、src/webgl/light.js、src/webgl/loading.js、src/webgl/material.js、src/webgl/p5.Camera.js(webgl/3d_primitives、interaction、light、loading、material、p5.Camera)
从_validateParameters出发的调用链大致如下:
validateParameters buildArgTypeCache addType lookupParamDoc scoreOverload testParamTypes testParamType getOverloadErrors _friendlyParamError ValidationError report friendlyWelcome语法
_validateParameters(func, args);参数
@param {String} func Name of the function being called @param {Array} args User input arguments示例
缺少参数示例:
arc(1, 1, 10.5, 10);FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 13] arc() was expecting at least 6 arguments, but received only 4. (https://p5js.org/reference/p5/arc)类型不匹配示例:
arc(1, ',1', 10.5, 10, 0, Math.PI);FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 14] arc() was expecting Number for the first parameter, received string instead. (https://p5js.org/reference/p5/arc)位置
src/friendly_errors/param_validator.js
fesErrorMonitor():全局错误监听与拼写检查
描述
fesErrorMonitor()监听浏览器错误消息,以推测错误的来源并向用户提供额外指引。这包括堆栈跟踪(stack trace)——即程序中一系列按顺序调用的函数列表,直到抛出错误的位置。堆栈跟踪对于判断错误是库内部错误还是由用户直接调用引起的错误非常有用。
它调用translator()使用键fes.globalErrors.*生成并打印友好错误消息(翻译键参见 translations/en/translation.json)。
以下是经由fesErrorMonitor()生成的错误消息的完整列表:
- 使用键
fes.globalErrors.syntax.*、fes.globalErrors.reference.*、fes.globalErrors.type.*的友好错误消息; - 通过
processStack()生成的"内部库"错误消息,使用键fes.wrongPreload、fes.libraryError; - 通过
printFriendlyStack()生成的堆栈跟踪消息,使用键fes.globalErrors.stackTop、fes.globalErrors.stackSubseq; - 通过
handleMisspelling()生成的拼写检查消息(源自引用错误),使用键fes.misspelling。
_fesErrorMonitor()会在window上的error事件与未处理的 Promise 拒绝(unhandledrejection事件)发生时自动触发。不过,它也可以在 catch 块中手动调用:
try { someCode(); } catch (err) { p5._fesErrorMonitor(err); }该函数目前支持ReferenceError、SyntaxError和TypeError的子集。支持的错误完整列表见 src/friendly_errors/browser_errors.js。在该文件中,每种浏览器错误都有对应的消息模式与类型分类,例如:
ReferenceError的NOTDEFINED("is not defined" / Safari 的 "Can't find variable")与CANNOTACCESS("Cannot access ... before initialization");SyntaxError的INVALIDTOKEN、UNEXPECTEDTOKEN、REDECLAREDVARIABLE、MISSINGINITIALIZER、BADRETURNORYIELD;TypeError的NOTFUNC、READNULL、READUDEFINED、CONSTASSIGN。
同一错误在不同浏览器的措辞不同,因此该表按浏览器(browser: 'Chrome' | 'Firefox' | 'Safari' | 'all')分别维护匹配模式。
_fesErrorMonitor的调用链大致如下:
_fesErrorMonitor processStack printFriendlyError (if type of error is ReferenceError) _handleMisspelling computeEditDistance _report _report printFriendlyStack (if type of error is SyntaxError, TypeError, etc) _report printFriendlyStack语法
fesErrorMonitor(event);参数
@param {*} e Error event示例
内部错误示例 1——在preload()中调用了background():
function preload() { // error in background() due to it being called in // preload background(200); }FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 8] An error with message "Cannot read properties of undefined (reading 'background')" occurred inside the p5js library when "background" was called. If not stated otherwise, it might be due to "background" being called from preload. Nothing besides load calls (loadImage, loadJSON, loadFont, loadStrings, etc.) should be inside the preload function. (https://p5js.org/reference/p5/preload)内部错误示例 2——mouseClicked()缺少回调参数:
function setup() { cnv = createCanvas(200, 200); cnv.mouseClicked(); }FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 12] An error with message "Cannot read properties of undefined (reading 'bind')" occurred inside the p5js library when mouseClicked was called. If not stated otherwise, it might be an issue with the arguments passed to mouseClicked. (https://p5js.org/reference/p5/mouseClicked)作用域错误示例——在draw()中访问了setup()内的局部变量:
function setup() { let b = 1; } function draw() { b += 1; }FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 5] "b" is not defined in the current scope. If you have defined it in your code, you should check its scope, spelling, and letter-casing (JavaScript is case-sensitive). + More info: https://p5js.org/examples/data-variable-scope.html拼写错误示例——把color()误写为xolor():
function setup() { xolor(1, 2, 3); }FES 会在控制台生成以下消息:
🌸 p5.js says: [sketch.js, line 2] It seems that you may have accidentally written "xolor" instead of "color". Please correct it to color if you wish to use the function from p5.js. (https://p5js.org/reference/p5/color)源码级原理:拼写检查如何工作
拼写检查依赖 fes_core.js 中的handleMisspelling()与computeEditDistance():
misusedAtTopLevelCode是一个惰性初始化的 p5 公开符号列表(函数/常量/变量),按名称长度降序排序,以确保优先报告最具体的匹配(例如误用HALF_PI时提示HALF_PI而非PI);computeEditDistance()使用 Wagner–Fischer 算法计算两个字符串间的 Levenshtein 距离;- 编辑距离阈值
EDIT_DIST_THRESHOLD = 2,只有当最近匹配的距离不超过该阈值(且小于等于符号自身长度)时才判定为拼写错误; - 只有一个最接近匹配时,消息附带参考文档链接;有多个匹配时,逐行列出每个建议(函数名后带
())。
位置
src/friendly_errors/fes_core.js
checkForUserDefinedFunctions():用户自定义函数的大小写检查
描述
检查是否有用户自定义函数(setup()、draw()、mouseMoved()等)被以大小写错误的方式使用。
它调用translator()使用键fes.checkUserDefinedFns生成并打印友好错误消息(翻译键参见 translations/en/translation.json)。
在源码中,该函数通过window.addEventListener('load', checkForUserDefinedFunctions, false)在页面加载时自动执行;同时,在实例模式下创建 p5 实例时(src/core/main.js 的构造函数中)也会调用p5._checkForUserDefinedFunctions(this)来检测setup、draw等的大小写错误。
语法
checkForUserDefinedFunctions(context);参数
@param {*} context Current default context. Set to window in "global mode" and to a p5 instance in "instance mode"示例
function preload() { loadImage('myimage.png'); }(注意:此处的误拼为preLoad——示例旨在演示 p5.js 2.0 之前的旧行为。在当前仓库中,preload()已被移除,p5 2.0 建议在setup()中使用async/await或回调加载资源;若用户仍定义了preload,fes_core.js 会打印迁移提示。)
FES 会在控制台生成以下消息:
🌸 p5.js says: It seems that you may have accidentally written preLoad instead of preload. Please correct it if it's not intentional. (https://p5js.org/reference/p5/preload)位置
src/friendly_errors/fes_core.js
helpForMisusedAtTopLevelCode():顶层代码误用检测
描述
helpForMisusedAtTopLevelCode()由fes_core.js在 window 加载时调用,用于检查是否有 p5.js 函数在setup()或draw()之外被使用。
它调用translator()使用键fes.misusedTopLevel生成并打印友好错误消息(翻译键参见 translations/en/translation.json)。
从源码看,该函数会遍历misusedAtTopLevelCode列表,用\W?{symbol}\W正则匹配错误消息文本,一旦命中即提示用户将 p5 符号移入setup(),并附上 FAQ 链接。由于不同浏览器对同一错误的措辞不同(例如 Chrome 的Uncaught ReferenceError: PI is not defined与 Firefox 的ReferenceError: PI is undefined),该实现刻意采用宽松的符号名匹配,源码注释中也提示这可能在少数情况下产生误报。
参数
@param {*} err Error event @param {Boolean} log false位置
src/friendly_errors/fes_core.js
开发笔记:已知局限与未来方向
误报(False Positive)与漏报(False Negative)
在 FES 中你会遇到两类错误:
- 误报(False Positive):像"假警报"。FES 警告你有一个错误,但你的代码实际上是正确的;
- 漏报(False Negative):像"漏掉了错误"。你的代码中存在错误,但 FES 没有向你发出警报。
识别并修复这两类错误非常重要,因为它们能节省调试时间、减少困惑,并让修复真正的问题变得更容易。
在某些不理想的情况下,错误处理的设计可能需要二选一:消除误报或消除漏报。如果必须选择,通常更倾向于消除误报——这样可以避免生成可能分散或误导用户注意力的错误警告。
与fes.GlobalErrors相关的局限
FES 只能检测使用const或var声明的被覆盖全局变量,使用let声明的变量无法被检测到。这一局限源于let处理变量实例化的特殊方式,目前无法解决。
fesErrorMonitor()描述的功能目前仅在 Web Editor 中或运行在本地服务器上时才有效。
FES 的性能问题
默认情况下,FES 在 p5.js 中启用,在p5.min.js中禁用,以防止 FES 函数拖慢进程。错误检查系统可能显著降低代码运行速度(在某些情况下最高可达约 10 倍)。
可以在草图顶部用一行代码禁用 FES:
p5.disableFriendlyErrors = true; // disables FES function setup() { // Do setup stuff } function draw() { // Do drawing stuff }在 src/core/main.js 的disableFriendlyErrors属性文档中说明了同样的用法,并指出禁用后circle(50, 50)这类缺参调用将静默失败而不显示友好错误。
请注意,禁用 FES 会关闭某些已知影响性能的功能,例如参数检查。但那些不影响性能的友好错误消息仍然会启用,包括:文件加载失败时的详细错误消息,以及在全局空间中试图覆盖 p5.js 函数时的警告。
从源码可以确认这个开关的实际作用范围:fes_core.js 中_friendlyError()、checkForUserDefinedFunctions()、fesErrorMonitor()的入口都带有if (p5.disableFriendlyErrors) return;守卫;param_validator.js 的参数校验同样受该开关控制;而 src/webgl/p5.Renderer3D.js 与 src/math/p5.Vector.js 也会查询该开关。此外,src/strands/p5.strands.js 在内部执行转换时会临时保存并恢复p5.disableFriendlyErrors的值,以避免 FES 干扰转换流程。
未来工作方向
原文档列出了以下未来改进方向:
- 解耦 FES(Decouple FES);
- 消除误报案例;
- 识别漏报案例;
- 增加更多单元测试以提升测试覆盖率;
- 更直观、清晰、可翻译的消息(友好错误的国际化讨论可参考社区整理的 FES i18n 资料);
- 识别更多常见错误类型并用 FES 泛化处理(例如
bezierVertex()、quadraticVertex()中必需对象未初始化的情况;检查nf()、nfc()、nfp()、nfs()的 Number 参数是否为正数)。
结论
本文以 contributor_docs/fes_contribution_guide.md 为骨架,梳理了 src/friendly_errors/ 目录的组织结构与每个核心函数的用途。FES 的核心入口函数——_report()、_friendlyError()、_friendlyFileLoadError()、_friendlyAutoplayError()、_validateParameters()、fesErrorMonitor()、checkForUserDefinedFunctions()、helpForMisusedAtTopLevelCode()——分别承担控制台输出、通用错误生成、文件加载错误、自动播放错误、参数校验、全局错误监听、大小写检查与顶层代码误用检测等职责。
同时,我们也结合贡献者笔记了解了 FES 的已知局限(误报/漏报权衡、let声明的全局变量无法检测、错误监听依赖本地服务器)与性能代价(可通过p5.disableFriendlyErrors = true关闭),以及未来在解耦、测试覆盖与消息国际化方面的改进方向。对于希望为 p5.js 贡献 FES 代码的开发者而言,这些内容既是源码导航图,也是参与改进的起点——例如从消除误报、补充单元测试这类明确可执行的方向入手。 </output_article>
【免费下载链接】p5.jsp5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. Looking for p5.js 2.0? http://beta.p5js.org项目地址: https://gitcode.com/GitHub_Trending/p5/p5.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考