本文是本人學(xué)習(xí)時遇到Promise后,在網(wǎng)上查詢資料及總結(jié)后的學(xué)習(xí)筆記。
什么是Promise?
看看MDN的定義:
原文請點擊
Promise 對象用于一個異步操作的最終完成(或失?。┘捌浣Y(jié)果值的表示。(簡單點說就是處理異步請求。。我們經(jīng)常會做些承諾,如果我贏了你就嫁給我,如果輸了我就嫁給你之類的諾言。這就是promise的中文含義。一個諾言,一個成功,一個失敗。)
我們再看看在控制臺上Promise長什么樣?

我們簡單總結(jié)一下:
1.Promise是一個構(gòu)造函數(shù),可以異步操作。
2.可以根據(jù)異步處理結(jié)果進行不同處理(success/error)
3.它包含了一些方法,(catch,then,reject,resolve...)
既然提及了根據(jù)不同異步結(jié)果,會有不同處理方法,那么我們?nèi)绾沃滥兀?br> 就是根據(jù)狀態(tài),沒錯,Promise就是根據(jù)狀態(tài)來識別該如何處理的。
一個 Promise有以下幾種狀態(tài):
- pending: 初始狀態(tài),不是成功或失敗狀態(tài)。
- fulfilled: 意味著操作成功完成。
- rejected: 意味著操作失敗。

而且Promise不能被重置,只能設(shè)置一次。
Once settled, a promise can not be resettled. Calling resolve() or reject() again will have no effect. The immutability of a settled promise is an important feature.
Promise怎么用
resolve用法
var myFirstPromise = new Promise(function(resolve, reject){
//當(dāng)異步代碼執(zhí)行成功時,我們才會調(diào)用resolve(...), 當(dāng)異步代碼失敗時就會調(diào)用reject(...)
//在本例中,我們使用setTimeout(...)來模擬異步代碼,實際編碼時可能是XHR請求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代碼正常執(zhí)行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面調(diào)用resolve(...)方法傳入的值.
//successMessage參數(shù)不一定非要是字符串類型,這里只是舉個例子
console.log("Yay! " + successMessage);
});
邏輯處理完畢并且沒有錯誤時,resolve這個回調(diào)會將值傳遞到一個特殊的地方。這個特殊的地方在哪呢?就是下面代碼中的then,我們使用then中的回調(diào)函數(shù)來處理resolve后的結(jié)果。比如上面的代碼中,我們將值簡單的輸出到控制臺。如果有錯誤,則reject到then的第二個回調(diào)函數(shù)中,對錯誤進行處理。
注意!我只是new了一個對象,并沒有調(diào)用它,我們傳進去的函數(shù)就已經(jīng)執(zhí)行了,這是需要注意的一個細節(jié)。所以我們用Promise的時候一般是包在一個函數(shù)中,在需要的時候去運行這個函數(shù),如:
function p() {
var myFirstPromise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("成功!"); //代碼正常執(zhí)行!
}, 250);
});
return myFirstPromise
}
p().then(function (successMessage) {
console.log("Yay! " + successMessage);
});
那我們返回的這個myFirstPromise有什么用呢?就是可以接著用then(),catch()等方法,這是Promise的精髓。
這和我們以前使用回調(diào)函數(shù)是相同的,有什么特殊呢?
function runAsync(callback){
setTimeout(function(){
console.log('執(zhí)行完成');
callback('隨便什么數(shù)據(jù)');
}, 2000);
}
runAsync(function(data){
console.log(data);
});
那么問題來了,有多層回調(diào)該怎么辦?如果callback也是一個異步操作,而且執(zhí)行完后也需要有相應(yīng)的回調(diào)函數(shù),該怎么辦呢?總不能再定義一個callback2,然后給callback傳進去吧。而Promise的優(yōu)勢在于,可以在then方法中繼續(xù)寫Promise對象并返回,然后繼續(xù)調(diào)用then來進行回調(diào)操作。
用Promise我們可以這么寫。
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)1執(zhí)行完成');
resolve('隨便什么數(shù)據(jù)1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)2執(zhí)行完成');
resolve('隨便什么數(shù)據(jù)2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)3執(zhí)行完成');
resolve('隨便什么數(shù)據(jù)3');
}, 2000);
});
return p;
}
runAsync1()//return a Promise object
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
結(jié)果如下:

當(dāng)然,直接返回數(shù)據(jù)也可以類似在函數(shù)里寫入resolve(''),傳給下面的回調(diào)函數(shù)。
runAsync1()
.then(function (data) {
console.log(data);
return '直接傳數(shù)據(jù)';
})
.then(function (data) {
console.log(data);
return runAsync3();
})
.then(function (data) {
console.log(data);
});
結(jié)果:

reject用法
接下來說說reject用法.
reject的作用就是把Promise的狀態(tài)置為rejected,這樣我們在then中就能捕捉到,然后執(zhí)行‘失敗’函數(shù)(此時我們調(diào)用的時then()傳入的第二個函數(shù))。所以這個狀態(tài)是我們標(biāo)記的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function getNumber() {
var p = new Promise(function (resolve, reject) {
//做一些異步操作
setTimeout(function () {
var num = Math.ceil(Math.random() * 10); //生成1-10的隨機數(shù)
if (num <= 5) {
resolve(num);
} else {
reject('數(shù)字是'+num+',太大了');
}
}, 2000);
});
return p;
}
getNumber()
.then(
function (data) {
console.log('resolved');
console.log(data);
},
function (reason, data) {
console.log('rejected');
console.log(reason);
}
);
</script>
</body>
</html>
結(jié)果

catch用法
我們知道Promise對象除了then方法,還有一個catch方法,它是做什么用的呢?其實它和then的第二個參數(shù)一樣,用來指定reject的回調(diào),用法是這樣:
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
});
這和我們上面在then里傳入第二個函數(shù)效果是一樣的。
不過它還有另外一個作用:在執(zhí)行resolve的回調(diào)(也就是上面then中的第一個參數(shù))時,如果拋出異常了(代碼出錯了),那么并不會報錯卡死js,而是會進到這個catch方法中。請看下面的代碼:
getNumber()
.then(function(data){
console.log('resolved');
console.log(data);
console.log(somedata); //此處的somedata未定義
})
.catch(function(reason){
console.log('rejected');
console.log(reason);
}).then(function(){
console.log('試著能不能繼續(xù)運行')
});
結(jié)果

我們看到第一個回調(diào)運行時產(chǎn)生錯誤以后,會把錯誤拋給catch(),然后處理后后面的程序會繼續(xù)運行,而不會停止。這是catch的好處.
否則會直接在控制臺報錯,后面也停止了。

all的用法
Promise的all方法提供了并行執(zhí)行異步操作的能力,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。我們?nèi)耘f使用上面定義好的runAsync1、runAsync2、runAsync3這三個函數(shù),看下面的例子:
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
結(jié)果

可以看到,我們把三個函數(shù)并行執(zhí)行時,把各自返回的結(jié)果存在一個數(shù)組里,再把這個數(shù)組作為參數(shù),傳遞給then。
以上~~~