js異常處理總結
先看最基礎的情況
function children() {
throw new Error("子報錯");
}
function parent() {
children(); //有異常拋出 函數中斷執行
}
parent();
console.log("cccccccc");
try catch 單層嵌套
function children() {
throw new Error("子報錯");
}
function parent() {
//可以在上一層函數捕獲下層函數的異常
try {
children(); //有異常拋出 函數中斷執行
} catch(error) {
console.log(error);
}
}
parent();
多級嵌套,捕獲下面的異常
function children() {
throw new Error("子報錯");
}
function parent() {
children(); //有異常拋出 函數中斷執行
}
//多級嵌套也是沒問題的, 異常回層層往上拋
try {
parent();
} catch (error) {
console.log(error);
}
- 預期異常:參數不合法,前提條件不符合,通常直接throw
- 非預期異常: js運行時異常,來著依賴庫異常
- 可以直接在異常上面提供一下附加屬性來提供上下文
function children() {
var err = new Error("子報錯");
err.statusCode = 404; //附加的屬性 提供上下文
throw err;
}
function parent() {
children(); //有異常拋出 函數中斷執行
}
try {
parent();
} catch (error) {
console.log(error.statusCode); //404
console.log(error);
}
異步回調異常處理
function asyncCallbackError(callback) {
setTimeout(() => {
throw new Error("異步出現了異常");
callback();
}, 0);
}
function callAsync() {
asyncCallbackError(function() {
});
}
//能不能在callAsync外面嵌套一個 try catch 處理異常呢?
try {
callAsync();
} catch(error) {
console.log(error);
}
上面的代碼是不行的,執行棧里面的try catch 無法捕獲異步隊列中拋出的異常
為啥 執行棧中的try catch無法捕獲到異步隊列的異常?
執行棧都執行完了,異步隊列才開始執行,所以執行棧無法捕獲異步
函數拋出的異常
所以對于異步函數,我們對異常的處理原則為, 異步函數里面使用自己的try catch
自處理異常,然后通過它的回調函數的參數 callback(error, value) 在上一層的調用
中判斷是否異步出現了異常,如果出現的話, error即第一個參數不為空
function asyncCallbackError(callback) {
setTimeout(() => {
//異步函數必須自己處理自己的異常
try {
//if (發送了異常) {
throw new Error("異步出現了異常");
//}
//else 沒有異常 {
//callback(null, value); 無異常的話 第一個參數設置為null 第二個自己的值
//}
} catch(error) {
callback(error);
}
}, 0);
}
function callAsync() {
//callback 判斷獲取異步異常值
asyncCallbackError((error, value) => {
if (error) { //如果異步出現異常
console.log(error);
} else {
}
});
}
callAsync();
promise 異常
promise本身只是一個 基于事件分發的狀態管理器
它不為我們管理異常,所以promise里面的異常必須我們自己try catch
當 發生異常的時候 就設置當前 promise的狀態為 reject
十分注意一點是: 如果一個 reject狀態的promise沒有進行處理,那么它
var p = new Promise((resolve, reject) => {
throw new Error("異常發生了");
resolve();
});
console.log(p);
上面的代碼 會直接拋出一個異常,程序無法執行,因為promsie不會為我們自動處理異常
var p = new Promise((resolve, reject) => {
try {
throw new Error("異常發生了");
} catch(error) {
//不讓它的狀態立即改變,防止reject進入異步隊列,這樣可以讓then注冊的回調不是立即執行
//tips: 一個promise狀態確定后,通過then注冊的回調 會立即執行
setTimeout(() => {
reject(error); //內部直接主動管理異常
}, 0);
}
});
p.then(() => {console.log("resolve")}, (error) => { console.log(error)});
promise內部發生異常,統一我們自己在內部try catch 處理,并設置它的狀態為reject
然后通過then 注冊reject回調處理,即promise異常處理通過reject狀態處理, 而且這里
再次強調一下,如果一個promise的reject狀態沒得到處理的話,會拋出一個異常
var p = new Promise(function (resolve, reject) {
setTimeout(reject, 0);
});
p.then(() => {});
上面的代碼報異常沒有捕獲,promise狀態為reject的話,需要處理reject情況
改成 p.then(()=>{}).catch(()=> {});
就可以了
注意: 目前node里面Promise的reject沒有處理,拋的異常不會阻止程序的執行,但是
未來這種情況會中斷node的執行
generator的異常
如果在一個generator 函數體內拋出一個異常 它會怎么樣呢?
var g = function* () {
throw new Error("異常發生了");
yield console.log('yielding');
};
var i = g();
console.log(i.next());
結果程序遇到異常 也直接不執行了,所以generator也不會幫我們處理異常
我們需要自己手動處理
var g = function* () {
try {
throw new Error("異常發生了");
} catch(error) {
yield console.log('yielding');
}
};
var i = g();
console.log(i.next());
generator 函數體內拋出的異常 還能在函數體外捕獲
但是注意捕獲的時間,哪個next 執行會拋出異常,就在哪次上門捕獲
當然也可以 try catch 包含多個next
var g = function* () {
throw new Error("異常發生了");
yield console.log('yielding');
};
var i = g();
try {
console.log(i.next());
} catch(error) {
console.log(error);
}
嵌套多個next 可能拋出的異常 異常
var g = function* () {
yield console.log('yielding');
throw new Error("異常發生了");
};
var i = g();
try {
i.next();
i.next(); //第二個才會拋出異常
} catch(error) {
console.log(error);
}
yield 自帶異常api Generator.prototype.throw()
Generator函數返回的遍歷器對象,都有一個throw方法,
可以在函數體外拋出錯誤,然后在Generator函數體內捕獲。
應該在 函數體里面的哪里捕獲呢? 想想 函數體外利用throw拋異常,
如何函數體內可以接受這個異常的話,那是不是說明現在的函數執行流程應該有
跑回到 generator函數里面,而generator會從上一個的yeild 左表達式開始執行,
所以,為了捕獲異常,我們應該 try catch 前一個yield
當然 它也是可以直接try catch 多個yeild 如果不確定那個拋出的話,只要在對應的
拋出異常的外傳yeild就可以了
var g = function* () {
try {
yield console.log('yielding'); //從這里斷開的 需要從左表達式處接受異常
} catch(error) {
console.log(error);
}
};
var i = g();
i.next();
i.throw(new Error("外部異常")); //throw 傳遞的參數可以傳遞到generator里面
如果連續利用多個 generator.throw 那么如果異常無法在generator函數體內進行捕獲,
那么它就會在函數generator體外拋出這個異常 我們可以在體外處理
注意: throw的話 相當于 一個next 并且 同時throw Error 所以它會對生成器函數內部迭代一次
var g = function* () {
try {
yield console.log('yielding'); //從這里斷開的 需要從左表達式處接受異常
} catch(error) {
console.log(error);
}
};
var i = g();
i.next();
i.throw(new Error("外部異常")); //throw 傳遞的參數可以傳遞到generator里面
i.throw(new Error("外部異常2")); //這個異常無法在generator函數里面捕獲,所以它往外面拋
//所以改寫為
var i = g();
i.next();
i.throw(new Error("外部異常")); //throw 傳遞的參數可以傳遞到generator里面
try {
i.throw(new Error("外部異常2")); //這個異常無法在generator函數里面捕獲,所以它往外面拋
} catch (error) {
console.log(error);
}
所以對于生成器函數來說 我們使用 它的 .throw函數拋出異常 在生成器函數里面使用 try catch 捕獲 yield異常,
內部無法捕獲的話,使用 try catch在對應的next處捕獲
await async異常
先直接在async里面拋出異常看看
async function f() {
throw new Error('出錯了');
}
f().then(
v => console.log(v),
e => console.log(e)
)
上面并不會報錯, 不會終止程序的執行,因為async函數是一個自執行的generator,它里面會捕獲異常,
然后返回一個reject的promise, 所以我們可以在async里面把異常轉為promise的reject
所以async無論如何都會返回一個promise,如果內部有異常,那么返回一個reject的promise,
value為異常error, 如果返回一個值,那么返回一個resolve的promise,value為這個值,如果
返回一個promise,那么async就直接返回這個promise
只要有一個await后面的promise是reject, 那么async就會中斷,并且返回一個reject的promise,
(正如之前說過promise為reject的話,而且未處理,那么它會拋出一個異常)
那 如果我想 即使 await后面是一個reject的promise,我如何還能讓它往下執行呢?
可以用一個 try catch 將對應的await 包裹住,這樣的話,它就可以捕獲promise未處理拋出的異常
或者把這個promise給處理了
async 處理promise reject異常的方法
try catch 方法
async function f() {
try {
await Promise.reject('出錯了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
把未處理的 promise給處理了 即利用.catch 這樣的話 返回一個resolve的promise
async function f() {
await Promise.reject('出錯了')
.catch(e => console.log(e));
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// 出錯了
// hello world
為何async內部會幫我們處理異常? 如何幫的?
//代碼來自阮一峰es6
function spawn(genF) {
return new Promise(function(resolve, reject) {
var gen = genF();
function step(nextF) {
//可以看到 每次迭代都將nextF用try catch 包裹起來,
//當生成器執行過程中拋出異常時,就可以在外部捕獲異常,并且立即返回 reject(e)
try {
var next = nextF();
} catch(e) {
return reject(e); //有異常的話 直接返回一個reject promise
}
//需要理解的是 為啥在這里捕獲異常??? 前面我們已經說過,生成器是一個迭代器,按照next的流程執行,
// 只有next執行的時候,generator才會開始執行,執行才可能發生異常,而且生成
// 器在next執行期間,內部發生的
//異常沒有被捕獲的話,可以往外拋,即再對應的next函數處,我們可以捕獲異常
// nextF()的執行 恰好就是執行generator的next函數
if(next.done) {
return resolve(next.value); //async的狀態直到 generator全部執行完 才確定
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
//發生錯誤,往生成器里面拋異常,如果這個異常在生成器內部沒有被捕獲,那么
//它可以往外拋
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}