try/catch
基本上是你們最常和async/await
一塊兒使用的,基本上咱們會用它去包圍大部分的異步方法。await
關鍵字後面的promise
一旦reject
了,就會拋出一個異常錯誤。javascript
run();
async function run() {
try {
await Promise.ject(new Error('Oops!'));
} catch (err) {
console.error(error.message);
}
}
複製代碼
try/catch
一樣也能夠處理同步的錯誤,好比下面 :html
async function run() {
const v = null;
try {
await Promise.resolve('foo');
v.thisWillThrow;
} catch (error) {
// 會出現"TypeError: Cannot read property 'thisWillThrow' of null"
console.error(error.message);
}
}
複製代碼
return
問題java
好像咱們只要無腦把邏輯都放到try/catch
裏面就萬事大吉了嗎?不太準確,下面的代碼卻會致使unhandled promise rejection
。這個return
關鍵字直接返回就錯誤卻不會被捕獲:node
async function run() {
try {
// 直接返回Promise,而不是用await關鍵字
return Promise.reject(new Error('Oops!'));
} catch (error) {
console.error(error.message);
}
}
複製代碼
這裏的解決方式是使用return await
來解決:golang
async function run() {
try {
return await Promise.reject(new Error('Oops!'));
} catch (error) {
console.error(error.message);
}
}
複製代碼
回調問題api
另一個問題是try catch
捕獲不了回調函數。try catch 僅僅在單一執行環境中奏效。這裏是在回調中加入try catch 來捕獲錯誤:跨域
setTimeout(funciton() {
try {
fn()
} catch (e) {
// handle error
}
})
複製代碼
這是奏效的,不過try catch
會在各個地方都出現。而V8引擎是不鼓勵try catch
在函數中的使用的。 以前試過把try catch
移到頂層來捕獲調用棧的錯誤,但這個處理對異步代碼不會奏效。數組
golang style即便用.then()
的方法來將一個promise
轉換爲另外一個處理完錯誤的reject promise
。可使用相似if(err)
來進行檢查:promise
async function throwAnError() {
throw new Error('Opps!');
}
async function runAwait() {
let err = await throwAnError();
if (err){
console.error(err.message);
}
}
複製代碼
這麼寫會直接拋出異常,由於這個方法拋出了異常,可是該方法自己沒有用try/catch
捕獲。不少時候,咱們在使用第三方庫的時候可能會出現這種狀況。瀏覽器
then()解決方法
async function runAwait() {
let err = await throwAnError().then(() => null, err => err);
if (err){
console.error(err.message);
}
}
複製代碼
then()
的方式,就會等待promise
狀態resolve
或reject
後而後執行相應的回調,而後判斷err
對象並處理,因此其實它至關於被捕獲了。
同時返回錯誤和值
async function run() {
let [err, res] = await throwAnError().then(v => [null, v], err => [err, null]);
if (err){
console.error(err.message);
}
console.log(res)
}
複製代碼
結果:
這麼作能夠經過解構返回一個數組,包含告終果和error
對象。固然若是是reject
就會返回null
和error
對象;而若是resolved
返回數組的第一個error
對象就爲null
,第二個就是結果。
優缺點
catch
。error
對象。run
方法中的同步錯誤。 因此這種方式須要謹慎使用。上面兩種模式均可以處理異步錯誤,可是對於錯誤處理,最好的狀況是在異步邏輯的最後加上catch
,這樣能夠保證全部錯誤都被捕獲到。其實這也是一個原則,即統一處理錯誤,而不是單獨去判斷並處理每一個錯誤。
async function run() {
return Promise.reject(new Error('Oops!'));
}
run().catch(function handleError(err) {
console.error(err.message);
}).catch( err => {
process.nextTick(() => { throw errl});
})
複製代碼
使用catch
捕獲錯誤,若是handleError
自己也有錯誤,就須要再catch
一遍,可是爲了不回調地獄
,若是該方法發生了錯誤就終止該進程。
優缺點
catch
的話,無論異步方法自己是否捕獲錯誤,它都會去捕獲異步錯誤。try/catch
沒法避免catch
自己拋出異常,而若是它拋出了那除了嵌套多一層try/catch
外,最好的作法就是加catch
來讓代碼更簡潔。瀏覽器全局處理基本上就是依靠事件,由於瀏覽器是事件驅動的。一旦拋出錯誤,解釋器在執行環境上下文中中止執行並展開,此時會有一個onerror
全局事件拋出:
window.addEventListener('error', function (e) {
var error = e.error;
console.log(error);
})
複製代碼
全局錯誤處理器會捕獲任何在執行環境中發生的錯誤,即使是不一樣的對象發生的錯誤事件,或者是各類類型的錯誤。這是全局集中處理錯誤的一種常見方式。
調用棧
調用棧在定位問題的時候十分重要,咱們可使用調用棧在處理器中處理特定的錯誤。
window.addEventListener('error', function (e) {
var stack = e.error.stack;
var message = e.error.toString();
if (stack) {
message += '\n' + stack;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/log', true);
// Fire an Ajax request with error details
xhr.send(message);
});
複製代碼
經過日誌能夠看到,具體什麼狀況觸發了什麼錯誤。在調試時調用堆棧也會很是有用。你 能夠分析log,看到什麼條件下觸發了錯誤。
注意:
若是跨域腳本是不會看到錯誤的。 在JS中,錯誤信息僅僅是容許在同一個域中。
我的想法
更多的時候,代碼拋出了異常,咱們更關注的是在運行時,某個變量的值是什麼,是否這個變量的值致使了錯誤,因此打印出調用時的跟多的信息更重要。
Node.js自己的異常處理要複雜得多,由於涉及到了進程或線程拋出異常的問題。
基於Koa的全局錯誤處理
nodejs是error-first
的異步處理機制,此處底層會調用net
模塊的listen
方法並在錯誤發生時執行回調。
app.listen(app.config.listenPort, (err) => {
if (err) throw err
app.logger.info(`> Ready on http://localhost:${app.config.listenPort}`)
})
複製代碼
路由錯誤處理
對於每一個路由,它可能也會有不一樣的錯誤處理邏輯,這時路由進來的請求就須要根據狀況返回不一樣的異常碼和信息。
router.get('/loginAuth', async (ctx, next) => {
try {
const code = query.code
const res = await requestToken(code)
if (res.data.code !== 0) {
ctx.app.logger.error(`request token error.Code is ${res.data.code} || response is: ${JSON.stringify(res.data.data)} || msg: ${res.data.message}`)
ctx.body = {
code: 10000,
message: `request token by code error`
}
} else {
ctx.body = res.data
}
} catch (err) {
ctx.app.logger.error(`request api has exception ${ctx.request.url} || ${err.code} || ${err.message} || ${err.stack}`)
ctx.body = {
code: 500,
message: `Error response`
}
}
})
複製代碼
try/catch
沒有問題。catch
來保證它們會被捕獲到。window
對象上,它會捕獲到異步錯誤,符合了DRY
和SOLID
原則。一個全局的錯誤處理器能夠幫你保持異步代碼整潔。