Promise.prototype.catch()
Promise.prototype.catch()方法是用於指定發生錯誤時的回調函數
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 處理 getJSON 和 前一個回調函數運行時發生的錯誤 console.log('發生錯誤!', error); });
上邊代碼中,getJSON( )方法返回一個Promise對象,如果該對象狀態為resolved,則會調用then()方法指定的回調函數;如果異步操作拋出錯誤,狀態就會變為rejected,就會調用catch( )方法指定的回調函數,處理這個錯誤。另外,then( )方法指定的回調函數,如果運行中拋出錯誤,也會被catch( )方法捕獲。
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同於
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
下面是一個例子
const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
上邊代碼中,promise拋出一個錯誤,就被catch方法指定的回調函數捕獲。
Promise對象的錯誤具有冒泡的性質,會一直向后傳遞,直到被捕獲為止。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 處理前面三個Promise產生的錯誤
});
上邊的代碼中,一共有三個Promise對象:一個由getJSON( )產生,兩個由then( )產生。它們中,任何一個拋出錯誤,都會被最后一個catch( )捕獲。
一般來說,不在then()方法里面定義Reject狀態的回調函數(即then的第二個參數),總是使用catch方法。
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
上邊的代碼,第二種寫法優於第一種,理由是第二種寫法可以捕獲前面then方法執行中的錯誤。因此,建議總是使用catch()方法,而不是使用then( )方法的第二個參數。
與傳統的try/catch代碼塊不同的是,如果沒有使用catch()方法指定錯誤處理的回調函數,Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應,
const someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行會報錯,因為x沒有聲明 resolve(x + 2); }); }; someAsyncThing().then(function() { console.log('everything is great'); }); setTimeout(() => { console.log(123) }, 2000); // Uncaught (in promise) ReferenceError: x is not defined // 123
上邊的代碼中,someAsyncThing( )函數產生的Promise對象,內部有語法錯誤,瀏覽器運行到這一行,會打印出錯誤提示,但是不會退出進程,終止腳本運行,2s之后還是會輸出123。 這就是說,Promise內部的錯誤不會影響到Promise外部的代碼,通俗的說法就是“Promise 會吃掉錯誤”
這個腳本放在服務器執行,退出碼是0(即表示執行成功)。不過,Node.js有一個unhandleRejection事件,專門監聽未捕獲的reject錯誤,上邊的腳本會觸發這個事件的監聽函數,可以在監聽函數里面拋出錯誤。
process.on('unhandledRejection', function (err, p) {
throw err;
});
上邊的代碼中,unhandleRejection事件的監聽函數有兩個參數,第一個是錯誤對象,第二個是報錯的Promise實例,它可以用來了解發生錯誤的環境信息。
注意,Node 有計划在未來廢除unhandleRejection事件。如果Promise內部有未捕獲的錯誤,會終止進程,並且進程的退出碼不為0.
一般總是建議,Promise對象后面要跟catch方法,這樣可以處理Promise內部發生的錯誤。catch( )方法返回的還是一個Promise對象,因此后面還可以接着調用then()方法。
const someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行會報錯,因為x沒有聲明 resolve(x + 2); }); }; someAsyncThing() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); // oh no [ReferenceError: x is not defined] // carry on
上邊的代碼運行完catch( )方法指定的回調函數,會接着運行后面那個then()方法指定的回調函數,如果沒有報錯,則會跳過catch( )方法
如下:
Promise.resolve() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); // carry on
上面的代碼因為沒有報錯,跳過了catch()方法,直接執行后面的then()方法。此時,要是then()方法里面報錯,就與前面的catch()無關了。
catch()方法之中,還能再拋出錯誤
someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); // 下面一行會報錯,因為y沒有聲明 y + 2; }).catch(function(error) { console.log('carry on', error); }); // oh no [ReferenceError: x is not defined] // carry on [ReferenceError: y is not defined]
上面代碼中,第二個catch()方法用來捕獲前一個catch()方法拋出的錯誤
