# 异常类型

# JS 代码执行异常

举个例子

1
2
var a = {};
a.b.c = 123;

针对 JS 代码执行异常我们可以 try...catch 包裹代码块,或者采用 window.addEventListener('error', listener) 来进行捕获

# Promise reject 异常

这类异常通常出现 promise 出现 reject 但是未被 catch 的情况
针对这类异常通常采用 window.addEventListener('unhandledrejection', listener) 来进行捕获

# 资源加载异常

这类异常出现在我们请求的 js css img 等链接资源失效的时候,这类异常并不会冒泡,所以需要在捕获阶段进行处理, window.addEventListener('error', listener, true) 第三个参数表示在捕获阶段进行处理

# 接口请求异常

  1. fetch 请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fetchReplacer(originalFetch: typeof fetch) {
return function (...args: Parameters<typeof fetch>) {
return originalFetch(...args)
.then(res => {
if (!res.ok) {
// TODO 错误处理
}
return res;
})
.catch(error => {
// TODO 错误处理
throw error;
});
};
}

const originalFetch = window.fetch;
if (!originalFetch) {
console.warn('fetch is not supported');
return;
}
window.fetch = fetchReplacer(originalFetch);
  1. xhr 请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
replacer(originalXHR: typeof XMLHttpRequest) {
const originalSend = originalXHR.prototype.send;
const originalOpen = originalXHR.prototype.open;
const shouldSkipHandler = (e: ProgressEvent<XMLHttpRequestEventTarget>) => {
const status = (e.target as XMLHttpRequest & ExtraXMLHttpRequest).status;
const type = e.type;
// 正常情况 + 超时情况/错误情况 触发的loadend
return (type === 'loadend' && status === 0) || (status + '').startsWith('2');
};
// 重写open方法 方便错误处理阶段获取接口请求信息
originalXHR.prototype.open = function open(
this: XMLHttpRequest & ExtraXMLHttpRequest,
method: string,
url: string | URL,
async: boolean = true,
username?: string | null,
password?: string | null
) {
this.method = method;
this.requestURL = typeof url === 'string' ? url : url.toString();
return originalOpen.apply(this, [method, url, async, username, password]);
};
originalXHR.prototype.send = function send(
this: XMLHttpRequest & ExtraXMLHttpRequest,
...args: Parameters<typeof originalSend>
) {
const handler = function (e: ProgressEvent<XMLHttpRequestEventTarget>) {
// 获取请求URL 请求状态 请求方法
const { responseURL, requestURL, status, statusText, method, responseText } =
e.target as XMLHttpRequest & ExtraXMLHttpRequest;
if (shouldSkipHandler(e)) return;
// TODO 错误处理
};
// 监听错误 (网络中断 / 跨域等)
this.addEventListener('error', handler);
// 监听请求错误 (请求体的内容非2xx)
this.addEventListener('loadend', handler);
// 监听请求超时
this.addEventListener('timeout', handler);
return originalSend.apply(this, args);
};
}

const originalXHR = window.XMLHttpRequest;
if (!originalXHR) {
console.warn('XMLHttpRequest is not supported');
return;
}
replacer(originalXHR);

# 跨域脚本执行异常

对于跨域的脚本 如果没有添加 crossorigin="anonymous" 则三方脚本的错误只显示 "Script error."
添加了 crossorigin="anonymous" 的脚本,对于 response 需要正确设置 Access-Control-Allow-Origin 允许当前网页进行跨域获取脚本

  1. anonymous 表示:
  • 不发送用户凭证(如 cookies)到其他域
  • 可以捕获脚本的详细错误信息
  1. 不使用 anonymous
  • 跨域脚本错误只显示 “Script error.”
  • 无法获取详细错误信息
  1. crossorigin="use-credentials"
  • 会发送用户凭证到其他域
  • 需要服务器返回 Access-Control-Allow-Credentials: true
1
<script src="http://www.example.com/app.js" crossorigin="anonymous"></script>

# 前端如何做全自动化的异常监控呢