ES6 Promise物件之例項方法(2)
ES6 Promise物件之例項方法(2)
上一篇關於Promise的文章介紹了Promise的基本用法,這一篇繼續上一篇,介紹Promise的各種方法:
(1)Promise.prototype.then()
then方法是定義在原型物件Promise.prototype上的。它的作用是為 Promise 例項新增狀態改變時的回撥函式。前一篇關於Promise物件的文章中提到過then方法的兩個引數,在此不再敘述。
then方法返回的是一個新的Promise例項(不是原來的Promise例項)。由此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法:
let promise = new Promise(function (resolve, reject) {
//some code
});
promise.then(function (value) {
//some code
}).then(function (value) {
//some code
});
使用then方法依次指定了兩個回撥函式,第一個回撥函式完成以後,會將返回結果作為引數傳入第二個回撥函式。如果前一個then回撥函式返回的還是一個Promise物件(即有非同步操作),這時後一個then回撥函式,就會等待該Promise物件的狀態發生變化,才會被呼叫。
(2)Promise.prototype.catch()
1.catch方法用於指定發生錯誤時的回撥函式。
let promise = new Promise(function (resolve, reject) {
//some code
});
promise.then(function (value) {
//some code
}).catch(function (value) {
//some code
});
如果Promise例項物件狀態變為Resolved,則呼叫then方法指定的回撥函式;
如果非同步操作丟擲錯誤,Promise例項物件狀態就會變為Rejected,就會呼叫catch方法指定的回撥函式處理這個錯誤。
另外,then方法指定的回撥函式如果在執行中丟擲錯誤,也會被catch方法捕獲。
2.catch方法的作用相當於then方法的第二個回撥函式:
promise.then((value) => console.log(value))
.catch((error) => console.log(error));
// 等同於
promise.then((value) => console.log(value))
.then(null, (error) => console.log(error));
3.reject方法的作用等同於丟擲錯誤
Promise丟擲一個錯誤被catch方法指定的函式捕獲,共有三種寫法(三種寫法是等價的):
let promise = new Promise((resolve, reject) => {
throw new Error("Error!")
});
promise.catch(function (error) {
console.log(error)
});
//Error: Error!
let promise = new Promise((resolve, reject) => {
try {
throw new Error("Error!")
}
catch (error) {
reject(error)
}
});
promise.catch(function (error) {
console.log(error)
});
//Error: Error!
let promise = new Promise((resolve, reject) => {
reject(new Error("Error!"))
});
promise.catch(function (error) {
console.log(error)
});
//Error: Error!
4.在resolve函式後面再丟擲錯誤,並不會被捕獲
let promise = new Promise((resolve, reject) => {
reject("Rejected!");
throw new Error("Error!")
});
promise.catch(function (error) {
console.log(error)
});
//Rejected!
5.Promise物件的錯誤會一直向後傳遞,直到被捕獲為止
也就是說,錯誤總會被下一個catch語句捕獲。
let promise = new Promise(function (resolve, reject) {
//some code
});
promise.then(function (value) {
//some code
}).then(function (value) {
//some code
}).catch(function (value) {
//some code
});
這裡的catch方法會處理前面Promise例項物件以及兩個then方法執行中丟擲的錯誤。
一般來說,不要在then方法中定義Rejected狀態的回撥函式(then的第二個引數),最好是用catch方法:
因為使用catch方法可以捕獲前面所有then方法中產生的錯誤,而採用then方法的第二個回撥函式無法對同一個then方法中的第一個回撥函式中的錯誤進行處理(對於前一個then方法中的錯誤可以進行處理)。
promise
.then(function(data){
//some code
}
.then(function(data){
//some code
},function(error){
//some code
});
promise
.then(function(data){
//some code
}
.then(function(data){
//some code
})
.catch(function(error){
//some code
});
第二種寫法要好於第一種寫法,因為第二種寫法catch方法可以捕獲前面所有then方法執行中的錯誤。
6.如果沒有用catch方法,丟擲的錯誤不會傳遞到外層程式碼
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);
在chrome中會列印如下:
// Uncaught (in promise) ReferenceError: x is not defined
// 123
而在火狐中列印如下(沒有列印錯誤提示):
//123
上面程式碼中,someAsyncThing函式產生的 Promise 物件,內部有語法錯誤。瀏覽器執行到這一行,不會退出程序、終止指令碼執行,2 秒之後還是會輸出123。這就是說,Promise 內部的錯誤不會影響到 Promise 外部的程式碼,通俗的說法就是“Promise 會吃掉錯誤”。
再來看下面的例子:
const promise = new Promise(function (resolve, reject) {
resolve('ok');
setTimeout(function () { throw new Error('test') }, 0)
});
promise.then(function (value) { console.log(value) });
setTimeout(() => { console.log(123) }, 2000);
// ok
// Uncaught Error: test
// 123
上面程式碼中,Promise 指定在下一輪“事件迴圈”再丟擲錯誤。到了那個時候,Promise 的執行已經結束了,所以這個錯誤是在 Promise 函式體外丟擲的,會冒泡到最外層,成了未捕獲的錯誤。
一般總是建議,Promise 物件後面要跟catch方法,這樣可以處理 Promise 內部發生的錯誤。
7.catch方法之後還可以呼叫then方法
catch執行完後返回的還是一個Promise物件,因此後面還可以呼叫then方法。
Promise.resolve()
.catch(function (error) {
console.log("oh no!" + error);
})
.then(function (data) {
console.log("carry on!")
});
//carry on!
程式碼執行完catch方法的回撥函式之後會接著執行後面那個then方法。
如果沒有報錯,就會跳過catch方法。
以上程式碼如果then中的回撥函式執行中出現錯誤,就與前面的catch無關了。
8.catch方法還可以再丟擲錯誤
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行會報錯,因為x沒有宣告
resolve(x + 2);
});
};
someAsyncThing().then(function() {
return someOtherAsyncThing();
}).catch(function(error) {
console.log('oh no', error);
// 下面一行會報錯,因為 y 沒有宣告
y + 2;
}).then(function() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
上面程式碼中,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方法丟擲的錯誤。
(3)Promise.resolve()
Promise.resolve用於將現有物件轉為Promise物件。下面兩種寫法等價:
Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))
1.引數是一個Promise例項
如果引數時Promise例項,那麼Promise.resolve將不做任何修改返回這個例項。
2.引數是一個thenable例項
thenable物件指的是具有then方法的物件,例如:
let thenable = {
then: function (resolve,reject) {
resolve(42);
}
}
將這個物件轉為Promise物件:
let thenable = {
then: function (resolve,reject) {
resolve(42);
}
};
let p1=Promise.resolve(thenable);
p1.then(function(value){
console.log(value);
})
//42
3.引數不具有then方法的物件或根本不是物件
如果引數是一個原始值,或者是一個不具有then方法的物件,Promise.resolve方法會返回一個新的Promise物件,狀態為Resolved。
下面我們先來看一下什麼是原始值:
在ECMAScript中,變數可以存放兩種型別的值:
a、原始值:固定而簡單的值,是存放在棧(stack)中的簡單資料段,它們的值直接儲存在變數訪問的位置。原始型別有以下五種型別:Undefined,Null,Boolean,Number,String;
b、引用值:存放在堆(heap)中的物件,儲存在變數處的是一個指標,指向儲存物件的記憶體地址。所有引用型別都整合自Object。
//字串
let p1=Promise.resolve("hello");
p1.then(function(value){
console.log(value);
})
//hello
//陣列
let p2=Promise.resolve([1,2,3]);
p2.then(function(value){
console.log(value);
})
//[1,2,3]
//不具有then方法的物件
let p3=Promise.resolve({"1":1});
p3.then(function(value){
console.log(value);
})
//{"1":1}
//null
let p4=Promise.resolve(null);
p4.then(function(value){
console.log(value);
})
//null
//undefined
let p5 = Promise.resolve(undefined);
p5.then(function (value) {
console.log(value);
})
//undefined
//數字
let p6 = Promise.resolve(123);
p6.then(function (value) {
console.log(value);
})
//123
//布林值
let p7 = Promise.resolve(true);
p7.then(function (value) {
console.log(value);
})
//true
以上Promise例項返回的狀態都是Resolved,所以會呼叫then方法的第一個回撥函式。
4.不帶有任何引數
直接返回一個Resolved狀態的Promise物件。因此,如果希望得到一個Promise物件,比較方便的方法就是直接呼叫Promise.resolve方法。
Promise.resolve()得到一個Promise物件變成Resolved狀態是在本輪“事件迴圈”結束時,而不是在下一輪“事件迴圈”開始時。
setTimeout(function(){
console.log("three");
},0);
Promise.resolve().then(function(){
console.log("two");
});
console.log("one")
//one
//two
//three
上面程式碼中,setTimeout()是在下一輪“事件迴圈”開始的時候執行的,Promise.resolve()在本輪“事件迴圈”結束的時候執行,console.log(“one”)則立即執行。
(4)Promise.reject()
Promise.reject方法返回一個新的Promise例項,狀態為Rejected:
下面兩種寫法等價:
const p = Promise.reject('出錯了');
// 等同於
const p = new Promise((resolve, reject) => reject('出錯了'))
Promise的例項物件p生成後狀態為Rejected,回撥函式會立即執行:
const p = Promise.reject('出錯了');
p.then(null, function (s) {
console.log(s)
});
// 出錯了
1.引數是一個Promise例項
將Promise例項的狀態變為Rejected,並返回一個新的Promise物件。reject函式的引數是Promise.reject()方法中傳入的引數(在這裡是Promises例項a):
let a = Promise.resolve();
const p = Promise.reject(a);
p.then(null, function (s) {
console.log(s);// Promise物件
console.log(s===a);//true
});
console.log(p===a);//false 說明返回新的Promise物件
2.引數是一個thenable例項
將thenable物件轉為Promise物件,新生成的Promise例項的狀態為Rejected。reject函式的引數是Promise.reject()方法中傳入的引數(在這裡是物件thenable1):
let thenable1 = {
then: function (resolve,reject) {
reject(42);
}
};
let p1=Promise.reject(thenable1);
p1.then(null,function(error){
console.log(error);//Object
console.log(error===thenable1);//true
})
注意這裡與Promise.resolve方法的不同:
//Promise.resolve方法
let thenable = {
then: function (resolve,reject) {
resolve(42);
}
};
let p1=Promise.resolve(thenable);
p1.then(function(value){
console.log(value);
});
//42
//Promise.reject方法
let thenable1 = {
then: function (resolve,reject) {
reject(42);
}
};
let p2=Promise.reject(thenable1);
p2.then(null,function(error){
console.log(error);//Object
console.log(error===thenable1);//true
})
reject函式的引數是Promise.reject()方法中傳入的引數(這裡是物件thenable1)。
3.引數不具有then方法的物件或根本不是物件
如果引數是一個原始值,或者是一個不具有then方法的物件,Promise.reject方法會返回一個新的Promise物件,狀態為Rejected。
//字串
let p1=Promise.reject("hello");
p1.then(null,function(value){
console.log(value);
});
//hello
//陣列
let p2=Promise.reject([1,2,3]);
p2.then(null,function(value){
console.log(value);
})
//[1,2,3]
//不具有then方法的物件
let p3=Promise.reject({"1":1});
p3.then(null,function(value){
console.log(value);
})