|
|
|
@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
# 明明程序报错,但是控制台不打印错误的几种情况(别怀疑,一定是代码问题,而不是程序运行太久)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这是一个很常见但容易让人困惑的问题。以下是程序报错但控制台不打印错误的几种常见情况:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 1. **异步错误未被捕获**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Promise 错误
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 错误不会打印到控制台
|
|
|
|
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
throw new Error('Promise error');
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要 catch 处理
|
|
|
|
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
throw new Error('Promise error');
|
|
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
|
|
console.error('Caught:', error);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### async/await 未捕获
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 错误不会打印
|
|
|
|
|
|
|
|
async function test() {
|
|
|
|
|
|
|
|
throw new Error('Async error');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
test();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要 try-catch 或外层 catch
|
|
|
|
|
|
|
|
async function test() {
|
|
|
|
|
|
|
|
throw new Error('Async error');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
test().catch(error => console.error('Caught:', error));
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2. **setTimeout/setInterval 中的错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 错误不会传播到外层
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
throw new Error('Timeout error');
|
|
|
|
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要在内部 try-catch
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
throw new Error('Timeout error');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('Caught in timeout:', error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3. **事件监听器中的错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 错误被事件系统吞掉
|
|
|
|
|
|
|
|
element.addEventListener('click', () => {
|
|
|
|
|
|
|
|
throw new Error('Event handler error');
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要内部 try-catch
|
|
|
|
|
|
|
|
element.addEventListener('click', () => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
throw new Error('Event handler error');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('Caught in event:', error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4. **微任务队列中的错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ queueMicrotask 中的错误
|
|
|
|
|
|
|
|
queueMicrotask(() => {
|
|
|
|
|
|
|
|
throw new Error('Microtask error');
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要包装处理
|
|
|
|
|
|
|
|
queueMicrotask(() => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
throw new Error('Microtask error');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error('Caught in microtask:', error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 5. **Web Workers 中的错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// worker.js
|
|
|
|
|
|
|
|
// ❌ 错误不会自动传递到主线程
|
|
|
|
|
|
|
|
throw new Error('Worker error');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 需要通过 onerror 或 postMessage
|
|
|
|
|
|
|
|
self.onerror = function(error) {
|
|
|
|
|
|
|
|
console.error('Worker error:', error);
|
|
|
|
|
|
|
|
return false; // 返回 false 让错误冒泡到主线程
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 6. **模块加载错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ ES6 模块中的顶级错误可能被静默处理
|
|
|
|
|
|
|
|
import './module-that-throws.js'; // 模块内部直接 throw
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 使用动态导入捕获
|
|
|
|
|
|
|
|
import('./module-that-throws.js')
|
|
|
|
|
|
|
|
.catch(error => console.error('Module load error:', error));
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 7. **被 try-catch 吞掉的错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 错误被捕获但没有处理
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
throw new Error('Some error');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
// 没有 console.error,错误就消失了
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ 更隐蔽的:catch 块自身出错
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
throw new Error('First error');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.nonExistentMethod(); // 这个错误不会被捕获!
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 8. **跨域脚本错误**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 跨域脚本的错误信息受限
|
|
|
|
|
|
|
|
<script src="https://other-domain.com/script.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 浏览器出于安全考虑,只会显示 "Script error."
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 9. **Console 被重写**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ 有人重写了 console.error
|
|
|
|
|
|
|
|
const originalError = console.error;
|
|
|
|
|
|
|
|
console.error = () => {}; // 静默所有错误
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查 console 是否被篡改
|
|
|
|
|
|
|
|
if (console.error.toString().includes('[native code]')) {
|
|
|
|
|
|
|
|
console.log('Console is native');
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
console.log('Console has been overwritten!');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 10. **错误在框架中被处理**
|
|
|
|
|
|
|
|
### React 的错误边界
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
|
|
|
|
// ❌ React 16+ 的错误被错误边界捕获
|
|
|
|
|
|
|
|
class ErrorBoundary extends React.Component {
|
|
|
|
|
|
|
|
componentDidCatch(error, info) {
|
|
|
|
|
|
|
|
// 如果没有 console.error,错误不会显示
|
|
|
|
|
|
|
|
// console.error('Error caught by boundary:', error);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
|
|
|
return this.props.children;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Vue 的错误处理
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ Vue 全局错误处理器
|
|
|
|
|
|
|
|
Vue.config.errorHandler = function (err, vm, info) {
|
|
|
|
|
|
|
|
// 如果没有处理,错误不会显示
|
|
|
|
|
|
|
|
// console.error('Vue error:', err);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 11. **Node.js 特定情况**
|
|
|
|
|
|
|
|
### 未监听的 'error' 事件
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// ❌ EventEmitter 的 error 事件需要监听
|
|
|
|
|
|
|
|
const stream = createReadStream('nonexistent.txt');
|
|
|
|
|
|
|
|
// 如果没有 error 监听器,错误会抛出并崩溃进程
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 必须监听 error 事件
|
|
|
|
|
|
|
|
stream.on('error', (error) => {
|
|
|
|
|
|
|
|
console.error('Stream error:', error);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Domain 模块(已废弃但可能遇到)
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
const domain = require('domain');
|
|
|
|
|
|
|
|
const d = domain.create();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ Domain 可能吞掉错误
|
|
|
|
|
|
|
|
d.on('error', (err) => {
|
|
|
|
|
|
|
|
// 如果没有处理,错误消失
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
d.run(() => {
|
|
|
|
|
|
|
|
throw new Error('Domain error');
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 调试和诊断技巧
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 1. **全局错误监听器**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// 捕获未处理的 Promise 错误
|
|
|
|
|
|
|
|
window.addEventListener('unhandledrejection', event => {
|
|
|
|
|
|
|
|
console.error('Unhandled promise rejection:', event.reason);
|
|
|
|
|
|
|
|
event.preventDefault(); // 阻止默认错误输出
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 捕获全局错误
|
|
|
|
|
|
|
|
window.addEventListener('error', event => {
|
|
|
|
|
|
|
|
console.error('Global error:', event.error);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Node.js
|
|
|
|
|
|
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
|
|
|
|
|
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
process.on('uncaughtException', (error) => {
|
|
|
|
|
|
|
|
console.error('Uncaught Exception:', error);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 2. **启用所有错误显示**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// Chrome DevTools
|
|
|
|
|
|
|
|
// 1. 打开 DevTools → Sources
|
|
|
|
|
|
|
|
// 2. 点击 Pause on exceptions 按钮(⏸️)
|
|
|
|
|
|
|
|
// 3. 勾选 "Pause on caught exceptions"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Node.js 调试
|
|
|
|
|
|
|
|
node --inspect app.js
|
|
|
|
|
|
|
|
// 或使用 ndb、node --inspect-brk
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 3. **检查控制台过滤器**
|
|
|
|
|
|
|
|
有时控制台过滤器可能被设置为隐藏错误:
|
|
|
|
|
|
|
|
- 检查 Console 顶部的过滤选项
|
|
|
|
|
|
|
|
- 确保 "Errors", "Warnings" 没有被过滤掉
|
|
|
|
|
|
|
|
- 尝试点击 "Default levels" → "All levels"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 4. **最小化复现**
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
// 创建一个最小测试
|
|
|
|
|
|
|
|
console.log('Script is running');
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
console.log('About to throw...');
|
|
|
|
|
|
|
|
throw new Error('Test error');
|
|
|
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
|
|
当错误不显示时,通常是因为:
|
|
|
|
|
|
|
|
1. **异步上下文**中的错误
|
|
|
|
|
|
|
|
2. **被某种机制捕获**但未处理
|
|
|
|
|
|
|
|
3. **框架或环境**的特定错误处理
|
|
|
|
|
|
|
|
4. **控制台配置**问题
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
最好的做法是始终添加全局错误监听器,并在开发时启用"捕获异常时暂停"功能。
|