fs.promises.readFile ENOENT 错误中没有堆栈
Posted
技术标签:
【中文标题】fs.promises.readFile ENOENT 错误中没有堆栈【英文标题】:no stack in fs.promises.readFile ENOENT error 【发布时间】:2021-09-02 10:07:15 【问题描述】:const fs = require('fs');
async function read()
return fs.promises.readFile('non-exist');
read()
.then(() => console.log('done'))
.catch(err =>
console.log(err);
)
给予:
➜ d2e2027b node app.js
[Error: ENOENT: no such file or directory, open 'non-exist']
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: 'non-exist'
➜ d2e2027b
堆栈丢失。如果我改用fs.readFileSync
,它会按预期显示堆栈。
➜ d2e2027b node app.js
Error: ENOENT: no such file or directory, open 'non-exist'
at Object.openSync (node:fs:582:3)
at Object.readFileSync (node:fs:450:35)
at read (/private/tmp/d2e2027b/app.js:4:13)
at Object.<anonymous> (/private/tmp/d2e2027b/app.js:8:1)
作为一个超级丑陋的解决方法,我可以在 ENOENT 的情况下使用 try/catch 并抛出一个新错误,但我确信那里有更好的解决方案。
read()
.then(() => console.log('done'))
.catch(err =>
if (err.code === 'ENOENT') throw new Error(`ENOENT: no such file or directory, open '$err.path'`);
console.log(err);
)
(我尝试了节点 v12、v14、v16 - 相同)
【问题讨论】:
【参考方案1】:Nodejs 有几个模块会抛出无用 stack
属性的错误;在我看来这是一个错误,但它从 nodejs 开始就存在,并且由于担心向后兼容性而可能无法更改(编辑:我收回这个;stack
属性是非标准的,开发人员应该知道不要依赖它的结构;nodejs 真的应该做出改变以抛出更有意义的错误)。
我已经包装了我在 nodejs 中使用的所有此类函数,并对其进行了修改以抛出好的错误。可以使用此函数创建此类包装器:
let formatErr = (err, stack) =>
// The new stack is the original Error's message, followed by
// all the stacktrace lines (Omit the first line in the stack,
// which will simply be "Error")
err.stack = [ err.message, ...stack.split('\n').slice(1) ].join('\n');
return err;
;
let traceableErrs = fnWithUntraceableErrs =>
return function(...args)
let stack = (new Error('')).stack;
try
let result = fnWithUntraceableErrs(...args);
// Handle Promises that resolve to bad Errors
let isCatchable = true
&& result != null // Intentional loose comparison
&& result.catch != null // Intentional loose comparison
&& (result.catch instanceof Function);
return isCatchable
? result.catch(err => throw formatErr(err, stack); )
: result;
catch(err)
// Handle synchronously thrown bad Errors
throw formatErr(err, stack);
;
这个包装器处理简单函数、返回承诺的函数和异步函数。基本前提是在调用包装函数时初步生成堆栈;此堆栈将具有导致调用包装函数的调用者链。现在,如果抛出错误(同步或异步),我们会捕获错误,将其stack
属性设置为有用的值,然后再次抛出它;如果你愿意的话,“抓到就放”。
这是我使用这种方法在终端中看到的:
> let readFile = traceableErrs(require('fs').promises.readFile);
> (async () => await readFile('C:/nonexistent.txt'))().catch(console.log);
Promise <pending>
> ENOENT: no such file or directory, open 'C:\nonexistent.txt'
at repl:5:18
at repl:1:20
at repl:1:48
at Script.runInThisContext (vm.js:120:20)
at REPLServer.defaultEval (repl.js:433:29)
at bound (domain.js:426:14)
at REPLServer.runBound [as eval] (domain.js:439:12)
at REPLServer.onLine (repl.js:760:10)
at REPLServer.emit (events.js:327:22)
at REPLServer.EventEmitter.emit (domain.js:482:12)
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\nonexistent.txt'
如果你想修改整个 fs.promises
套件以抛出好的错误,你可以这样做:
let fs = ...require('fs').promises ;
for (let k in fs) fs[k] = traceableErrs(fs[k]);
(async () =>
// Now all `fs` functions throw stackful errors
await fs.readFile(...);
await fs.writeFile(...);
)();
【讨论】:
感谢@Gershy,这是一种有趣的方法,您基本上是将堆栈从new Error('')
分配给当前错误。我快速测试了它,似乎消息消失了。编辑:我将其更改为以下throw Object.assign(err, stack: new Error(err.message).stack );
,消息又回来了。我不能说我喜欢它,但也许目前没有更好的选择。
@David 唯一的问题是,如果您在 catch
回调中调用 new Error
,堆栈的用处将大大降低 - 我正在快速编辑;让我知道它是否适合您...以上是关于fs.promises.readFile ENOENT 错误中没有堆栈的主要内容,如果未能解决你的问题,请参考以下文章