var p = new Promise(function(resolve, reject){
console.log("create a promise");
resolve("success");
});
console.log("after new Promise");
p.then(function(value){
console.log(value);
});
複製代碼
控制檯輸出:前端
"create a promise"
"after new Promise"
"success"
複製代碼
Promise對象表示將來某個將要發生的事件,但在建立(new)Promise時,做爲Promise參數傳入的函數是會被當即執行的,只是其中執行的代碼能夠是異步代碼。有些同窗會認爲,當Promise對象調用then方法時,Promise接收的函數纔會執行,這是錯誤的。所以,代碼中"create a promise"先於"after new Promise"輸出。promise
var p1 = new Promise(function(resolve,reject){
resolve(1);
});
var p2 = new Promise(function(resolve,reject){
setTimeout(function(){
resolve(2);
}, 500);
});
var p3 = new Promise(function(resolve,reject){
setTimeout(function(){
reject(3);
}, 500);
});
console.log(p1);
console.log(p2);
console.log(p3);
setTimeout(function(){
console.log(p2);
}, 1000);
setTimeout(function(){
console.log(p3);
}, 1000);
p1.then(function(value){
console.log(value);
});
p2.then(function(value){
console.log(value);
});
p3.catch(function(err){
console.log(err);
});
複製代碼
控制檯輸出:bash
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
1
2
3
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}
複製代碼
Promise的內部實現是一個狀態機。Promise有三種狀態:pending,resolved,rejected。當Promise剛建立完成時,處於pending狀態;當Promise中的函數參數執行了resolve後,Promise由pending狀態變成resolved狀態;若是在Promise的函數參數中執行的不是resolve方法,而是reject方法,那麼Promise會由pending狀態變成rejected狀態。異步
p二、p3剛建立完成時,控制檯輸出的這兩臺Promise都處於pending狀態,但爲何p1是resolved狀態呢? 這是由於p1 的函數參數中執行的是一段同步代碼,Promise剛建立完成,resolve方法就已經被調用了,於是緊跟着的輸出顯示p1是resolved狀態。咱們經過兩個setTimeout
函數,延遲1s後再次輸出p二、p3的狀態,此時p二、p3已經執行完成,狀態分別變成resolved和rejected。函數
var p1 = new Promise(function(resolve, reject){
resolve("success1");
resolve("success2");
});
var p2 = new Promise(function(resolve, reject){
resolve("success");
reject("reject");
});
p1.then(function(value){
console.log(value);
});
p2.then(function(value){
console.log(value);
});
複製代碼
控制檯輸出:ui
"success1"
"success"
複製代碼
Promise狀態的一旦變成resolved或rejected時,Promise的狀態和值就固定下來了,不論你後續再怎麼調用resolve或reject方法,都不能改變它的狀態和值。所以,p1中resolve("success2")
並不能將p1的值更改成success2
,p2中reject("reject")
也不能將p2的狀態由resolved改變爲rejected.spa
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一個then
console.log(value);
return value*2;
}).then(function(value){ //第二個then
console.log(value);
}).then(function(value){ //第三個then
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ //第四個then
console.log(value);
return Promise.reject('reject');
}).then(function(value){ //第五個then
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err);
})
複製代碼
控制檯輸出:code
1
2
undefined
"resolve"
"reject: reject"
複製代碼
Promise對象的then方法返回一個新的Promise對象,所以能夠經過鏈式調用then方法。then方法接收兩個函數做爲參數,第一個參數是Promise執行成功時的回調,第二個參數是Promise執行失敗時的回調。兩個函數只會有一個被調用,函數的返回值將被用做建立then返回的Promise對象。這兩個參數的返回值能夠是如下三種狀況中的一種:cdn
return
一個同步的值 ,或者 undefined
(當沒有返回一個有效值時,默認返回undefined),then
方法將返回一個resolved狀態的Promise對象,Promise對象的值就是這個返回值。return
另外一個 Promise,then
方法將根據這個Promise的狀態和值建立一個新的Promise對象返回。throw
一個同步異常,then
方法將返回一個rejected狀態的Promise, 值是該異常。根據以上分析,代碼中第一個then
會返回一個值爲2(1*2),狀態爲resolved的Promise對象,因而第二個then
輸出的值是2。第二個then
中沒有返回值,所以將返回默認的undefined,因而在第三個then
中輸出undefined。第三個then
和第四個then
中分別返回一個狀態是resolved的Promise和一個狀態是rejected的Promise,依次由第四個then
中成功的回調函數和第五個then
中失敗的回調函數處理。對象
var p = new Promise(function(resolve, reject){
resolve("success");
});
p.then(function(value){
console.log(value);
});
console.log("which one is called first ?");
複製代碼
控制檯輸出:
"which one is called first ?"
"success"
複製代碼
Promise接收的函數參數是同步執行的,但then
方法中的回調函數執行則是異步的,所以,"success"會在後面輸出。
var p1 = new Promise( function(resolve,reject){
foo.bar();
resolve( 1 );
});
p1.then(
function(value){
console.log('p1 then value: ' + value);
},
function(err){
console.log('p1 then err: ' + err);
}
).then(
function(value){
console.log('p1 then then value: '+value);
},
function(err){
console.log('p1 then then err: ' + err);
}
);
var p2 = new Promise(function(resolve,reject){
resolve( 2 );
});
p2.then(
function(value){
console.log('p2 then value: ' + value);
foo.bar();
},
function(err){
console.log('p2 then err: ' + err);
}
).then(
function(value){
console.log('p2 then then value: ' + value);
},
function(err){
console.log('p2 then then err: ' + err);
return 1;
}
).then(
function(value){
console.log('p2 then then then value: ' + value);
},
function(err){
console.log('p2 then then then err: ' + err);
}
);
複製代碼
控制檯輸出:
p1 then err: ReferenceError: foo is not defined
p2 then value: 2
p1 then then value: undefined
p2 then then err: ReferenceError: foo is not defined
p2 then then then value: 1
複製代碼
Promise中的異常由then
參數中第二個回調函數(Promise執行失敗的回調)處理,異常信息將做爲Promise的值。異常一旦獲得處理,then
返回的後續Promise對象將恢復正常,並會被Promise執行成功的回調函數處理。另外,須要注意p一、p2 多級then
的回調函數是交替執行的 ,這正是由Promise then
回調的異步性決定的。
var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){
resolve(1);
});
var p4 = new Promise(function(resolve, reject){
resolve(p1);
});
console.log(p1 === p2);
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);
p4.then(function(value){
console.log('p4=' + value);
});
p2.then(function(value){
console.log('p2=' + value);
})
p1.then(function(value){
console.log('p1=' + value);
})
複製代碼
控制檯輸出:
true
false
false
false
p2=1
p1=1
p4=1
複製代碼
Promise.resolve(...)
能夠接收一個值或者是一個Promise對象做爲參數。當參數是普通值時,它返回一個resolved狀態的Promise對象,對象的值就是這個參數;當參數是一個Promise對象時,它直接返回這個Promise參數。所以,p1 === p2。但經過new的方式建立的Promise對象都是一個新的對象,所以後面的三個比較結果都是false。另外,爲何p4的then
最早調用,但在控制檯上是最後輸出結果的呢?由於p4的resolve
中接收的參數是一個Promise對象p1,resolve
會對p1」拆箱「,獲取p1的狀態和值,但這個過程是異步的,可參考下一節。
var p1 = new Promise(function(resolve, reject){
resolve(Promise.resolve('resolve'));
});
var p2 = new Promise(function(resolve, reject){
resolve(Promise.reject('reject'));
});
var p3 = new Promise(function(resolve, reject){
reject(Promise.resolve('resolve'));
});
p1.then(
function fulfilled(value){
console.log('fulfilled: ' + value);
},
function rejected(err){
console.log('rejected: ' + err);
}
);
p2.then(
function fulfilled(value){
console.log('fulfilled: ' + value);
},
function rejected(err){
console.log('rejected: ' + err);
}
);
p3.then(
function fulfilled(value){
console.log('fulfilled: ' + value);
},
function rejected(err){
console.log('rejected: ' + err);
}
);
複製代碼
控制檯輸出:
p3 rejected: [object Promise]
p1 fulfilled: resolve
p2 rejected: reject
複製代碼
Promise回調函數中的第一個參數resolve
,會對Promise執行"拆箱"動做。即當resolve
的參數是一個Promise對象時,resolve
會"拆箱"獲取這個Promise對象的狀態和值,但這個過程是異步的。p1"拆箱"後,獲取到Promise對象的狀態是resolved,所以fulfilled
回調被執行;p2"拆箱"後,獲取到Promise對象的狀態是rejected,所以rejected
回調被執行。但Promise回調函數中的第二個參數reject
不具有」拆箱「的能力,reject的參數會直接傳遞給then
方法中的rejected
回調。所以,即便p3 reject
接收了一個resolved狀態的Promise,then
方法中被調用的依然是rejected
,而且參數就是reject
接收到的Promise對象。
歡迎關注個人公衆號:老幹部的大前端,領取21本大前端精選書籍!