node.js - fs.promises.readFile ENOENT 错误中没有堆栈
问题描述
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 的情况下抛出一个新错误,但我确信那里有更好的解决方案。
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 - 相同)
解决方案
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(...);
})();
推荐阅读
- reactjs - 是否可以在 ReactJS 中进行多个 proptype 类型检查
- c++ - std::bind,无法使用单个参数的方法工作
- laravel - GuzzleHttp\Exception\ConnectException cURL 错误 7:无法连接到 localhost 端口 8088:连接被拒绝
- c++ - 在 CRTP 中使用嵌套名称说明符
- opencv - 如何将 OpenCV 库静态链接到我的项目?
- angular - 实现角度数据表时没有显示数据选项?
- java - 没有 Java Swing 的鼠标监听器
- php - slim框架Index.php文件中如何使用或连接phpmailers文件
- android - 使用不同的 google-services.json 签名的 apk 版本时,Android 应用程序冻结
- angular - routerlink:错误错误:“[对象对象]”