一塌忽、什么是Promise
MDN對Promise的定義:Promise對象用于異步操作刃唐,它表示一個尚未完成且預(yù)計在未來完成的異步操作。Promise的出現(xiàn)解決了回調(diào)地獄的問題熟吏,使用Promise拐揭,我們可以利用then進(jìn)行鏈?zhǔn)交卣{(diào),將異步操作以同步操作的流程表示出來火脉。
二牵舵、Promise原理分析
Promise原理說起來并不難,它內(nèi)部有三個狀態(tài)倦挂,分別是pending畸颅,fulfilled和rejected 。
pending是對象創(chuàng)建后的初始狀態(tài)方援,當(dāng)對象fulfill(成功)時變?yōu)閒ulfilled没炒,當(dāng)對象reject(失敗)時變?yōu)閞ejected犯戏。且只能從pengding變?yōu)閒ulfilled或rejected 送火,而不能逆向或從fulfilled變?yōu)閞ejected 、從rejected變?yōu)閒ulfilled先匪。一旦狀態(tài)改變漾脂,就凝固了,會一直保持這個狀態(tài)胚鸯,不會再發(fā)生變化骨稿。當(dāng)狀態(tài)發(fā)生變化,promise.then綁定的函數(shù)就會被調(diào)用姜钳。
注意:Promise一旦新建就會「立即執(zhí)行」坦冠,無法取消。這也是它的缺點之一哥桥。
三 辙浑、Promise的基本用法
1、基本用法
//構(gòu)建Promise
var promise = new Promise(function (resolve, reject) {
if (/* 異步操作成功 */) {
resolve(data);
} else {
/* 異步操作失敗 */
reject(error);
}
});
promise.then(function(data) {
// do something when success
}, function(error) {
// do something when failure
});
- 類似構(gòu)建對象拟糕,我們使用new來構(gòu)建一個Promise判呕。Promise接收一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject送滞。這兩個函數(shù)就是就是回調(diào)函數(shù)侠草,由JavaScript引擎提供犁嗅。resolve函數(shù)的作用:在異步操作成功時調(diào)用边涕,并將異步操作的結(jié)果,作為參數(shù)傳遞出去; reject函數(shù)的作用:在異步操作失敗時調(diào)用式撼,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。Promise實例生成以后荤牍,可以用then方法指定resolved狀態(tài)和reject狀態(tài)的回調(diào)函數(shù)案腺。
- then方法會返回一個Promise。它有兩個參數(shù)康吵,分別為Promise從pending變?yōu)閒ulfilled和rejected時的回調(diào)函數(shù)(第二個參數(shù)非必選)劈榨。這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)。簡單來說晦嵌,then就是定義resolve和reject函數(shù)的同辣。
- Promise新建后就會立即執(zhí)行。而then方法中指定的回調(diào)函數(shù)惭载,將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會執(zhí)行旱函。如下例:
var promise = new Promise(function(resolve, reject) {
console.log('before resolved');
resolve();
console.log('after resolved');
});
promise.then(function() {
console.log('resolved');
});
console.log('outer');
-------output-------
before resolved
after resolved
outer
resolved
2、基本API
- .then()
對promise添加onFulfilled和onRejected回調(diào)描滔,并返回的是一個新的Promise實例(不是原來那個Promise實例)棒妨,且返回值將作為參數(shù)傳入這個新Promise的resolve函數(shù),可以使用鏈?zhǔn)綄懛êぁS捎谇耙粋€回調(diào)函數(shù)券腔,返回的還是一個Promise對象,這時后一個回調(diào)函數(shù)拘泞,就會等待該Promise對象的狀態(tài)發(fā)生變化纷纫,才會被調(diào)用。 - .catch()
該方法是.then(undefined, onRejected)的別名田弥,用于指定發(fā)生錯誤時的回調(diào)函數(shù)涛酗。
promise對象的錯誤,會一直向后傳遞偷厦,直到被捕獲商叹。即錯誤總會被下一個catch所捕獲。then方法指定的回調(diào)函數(shù)只泼,若拋出錯誤剖笙,也會被下一個catch捕獲。catch中也能拋錯请唱,則需要后面的catch來捕獲弥咪。 - .all()
該方法用于將多個Promise實例,包裝成一個新的Promise實例十绑。
Promise.all方法接受一個數(shù)組(或具有Iterator接口)作參數(shù)聚至,數(shù)組中的對象(p1、p2本橙、p3)均為promise實例(如果不是一個promise扳躬,該項會被用Promise.resolve轉(zhuǎn)換為一個promise)。它的狀態(tài)由這三個promise實例決定甚亭。當(dāng)p1, p2, p3狀態(tài)都變?yōu)閒ulfilled贷币,p的狀態(tài)才會變?yōu)閒ulfilled,并將三個promise返回的結(jié)果亏狰,按參數(shù)的順序(而不是 resolved的順序)存入數(shù)組役纹,傳給p的回調(diào)函數(shù)。當(dāng)p1, p2, p3其中之一狀態(tài)變?yōu)閞ejected暇唾,p的狀態(tài)也會變?yōu)閞ejected促脉,并把第一個被reject的promise的返回值,傳給p的回調(diào)函數(shù)策州。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function (resolve, reject) {
resolve('second');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "third");
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values);
});
-------output-------
//約 3s 后
["first", "second", "third"]
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(reject, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
reject("three");
});
Promise.all([p1, p2, p3]).then(function (value) {
console.log('resolve', value);
}, function (error) {
console.log('reject', error); // => reject three
});
-------output-------
reject three
- .race()
Promise.race方法同樣接受一個數(shù)組(或具有Iterator接口)作參數(shù)嘲叔。當(dāng)p1, p2, p3中有一個實例的狀態(tài)發(fā)生改變(變?yōu)閒ulfilled或rejected),p的狀態(tài)就跟著改變抽活。并把第一個改變狀態(tài)的promise的返回值硫戈,傳給p的回調(diào)函數(shù)。
/* 例3.11 */
var p1 = new Promise(function(resolve, reject) {
setTimeout(reject, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {
console.log('resolve', value);
}, function(error) {
//not called
console.log('reject', error);
});
-------output-------
resolve two
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, "four");
});
Promise.race([p3, p4]).then(function(value) {
//not called
console.log('resolve', value);
}, function(error) {
console.log('reject', error);
});
-------output-------
reject four
- .resolve()
它可以看做new Promise()的快捷方式下硕。
Promise.resolve('Success');
/*******等同于*******/
new Promise(function (resolve) {
resolve('Success');
});
這段代碼會讓這個Promise對象立即進(jìn)入resolved狀態(tài)丁逝,并將結(jié)果success傳遞給then指定的onFulfilled回調(diào)函數(shù)。由于Promise.resolve()也是返回Promise對象梭姓,因此可以用.then()處理其返回值霜幼。
/* 例3.13 */
Promise.resolve('success').then(function (value) {
console.log(value);
});
-------output-------
Success
- .reject()
這個方法和上述的Promise.resolve()類似,它也是new Promise()的快捷方式誉尖。
Promise.reject(new Error('error'));
/*******等同于*******/
new Promise(function (resolve, reject) {
reject(new Error('error'));
});
這段代碼會讓這個Promise對象立即進(jìn)入rejected狀態(tài)罪既,并將錯誤對象傳遞給then指定的onRejected回調(diào)函數(shù)。
四 Promise常見問題
1、reject 和 catch 的區(qū)別
- promise.then(onFulfilled, onRejected)在onFulfilled中發(fā)生異常的話琢感,在onRejected中是捕獲不到這個異常的丢间。
- promise.then(onFulfilled).catch(onRejected).then中產(chǎn)生的異常能在.catch中捕獲一般情況,還是建議使用第二種驹针,因為能捕獲之前的所有異常烘挫。當(dāng)然了,第二種的.catch()也可以使用.then()表示柬甥,它們本質(zhì)上是沒有區(qū)別的饮六,.catch === .then(null, onRejected)
2、promise狀態(tài)變?yōu)閞esove或reject苛蒲,就凝固了卤橄,不會再改變
console.log(1);
new Promise(function (resolve, reject){
reject();
setTimeout(function (){
resolve(); //not called
}, 0);
}).then(function(){
console.log(2);
}, function(){
console.log(3);
});
console.log(4);
-------output-------
1
4
3
3、在異步回調(diào)中拋錯臂外,不會被catch到
// Errors thrown inside asynchronous functions will act like uncaught errors
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
promise.catch(function(e) {
console.log(e); //This is never called
});