當我還是一個小白的時候,我翻了很多關(guān)于Promise
介紹的文檔,我一直沒能理解所謂解決異步操作的痛點是什么意思
直到我翻了谷歌第一頁的所有中文文檔我才有所頓悟,其實從他的英文字面意思理解最為簡單粗暴
這就是一個承諾,相當于在代碼中提供一個在任何時候承諾之后要做什么事的方式,這個承諾可能會兌現(xiàn)也可能無法兌現(xiàn),當然也可能在兌現(xiàn)的過程中
用這個來替代我們曾經(jīng)需要寫的回調(diào)函數(shù),可以避免JavaScript程序中的回調(diào)地獄
所以先不去學習的語法,從另一個方式先理解,希望可以幫助你更好的學習或上手Promise
什么是Promise?
Promise
是異步編程的一種解決方案,可以替代傳統(tǒng)的解決方案,比如回調(diào)函數(shù)和事件
其在ES6中統(tǒng)一了用法,并提供了原生的Promise對象
,Promise對象
表示異步操作的最終完成(或失敗)及其結(jié)果值
作為對象,Promise
有以下兩個特點:
- 對象的狀態(tài)不受外界影響
- 只有異步操作的結(jié)果,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài),
這也是Promise
這個名字的由來,它的英語意思就是承諾,表示其他手段無法改變
- 只有異步操作的結(jié)果,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài),
- 一旦狀態(tài)改變了就不會在變,也就是說任何時候Promise都只有一種狀態(tài)
-
pending
:初始狀態(tài),不是成功或失敗狀態(tài);
fulfilled(resolved)
:操作成功完成狀態(tài);
rejected
:操作失敗狀態(tài) -
Promise對象
的狀態(tài)改變,只有兩種可能:從Pending
變?yōu)?code>Resolved和從Pending
變?yōu)?code>Rejected,
只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,
就算改變已經(jīng)發(fā)生了,你再對Promise對象
添加回調(diào)函數(shù),也會立即得到這個結(jié)果,
這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的
-
Promise的創(chuàng)建和用途
- 構(gòu)造函數(shù)接受一個名為
executor
的函數(shù),此執(zhí)行函數(shù)接受兩個f函數(shù)
參數(shù),resolve
和reject
。
new Promise( /* executor */ function(resolve, reject { ... }) );
- Promise通常用于阻塞代碼和異步操作,其中包括文件調(diào)用,API調(diào)用,DB調(diào)用,IO調(diào)用等等
- 這些異步操作的啟動發(fā)生在執(zhí)行函數(shù)中,如果異步操作成功,則通過
promise
的創(chuàng)建者調(diào)用resolve()
函數(shù)返回預(yù)期結(jié)果,
同樣,如果出現(xiàn)意外錯誤,則通過調(diào)用reject()
函數(shù)傳遞錯誤具體信息 - 由于
promise
會立即執(zhí)行,我們無法檢查promise
的初始狀態(tài),所以創(chuàng)建一個需要時間執(zhí)行的promise
最簡單的方式就是
使用setTimeOut()
函數(shù),通過瀏覽器控制臺的輸出可以直觀看到之前所說的狀態(tài),以及他的變化過程
var promiseFir = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
message: '與大家分享知識很開心!',
code: 200
});
}, 2000)
})
console.log(promiseFir);
setTimeout(function() {
console.log(promiseFir);
}, 2000)
Promise的方法
- 3種原型方法
// 用一個小故事舉例,你是一個上學的孩子,你問你的媽媽要一個電話。她說: 這個月底我要買一部手機
var momsPromise = new Promise(function(resolve, reject) {
momsSavings = 20000; // 因為媽媽沒有足夠儲蓄所以無法買到禮物
// momsSavings = 200000; // 如果媽媽有足夠的儲蓄就可以買到禮物
priceOfPhone = 60000;
if (momsSavings > priceOfPhone) {
resolve({
brand: "iphone",
model: "6s"
});
} else {
reject("我們沒有足夠的儲蓄,讓我們多存點錢吧。");
}
});
momsPromise.then(function(value) {
console.log("哇,我得到這個電話作為禮物 ", JSON.stringify(value));
});
momsPromise.catch(function(reason) {
console.log("媽媽不能給我買電話,因為 ", reason);
});
momsPromise.finally(function() {
console.log(
"不管媽媽能不能給我買個電話,我仍然愛她"
);
});
- 4種靜態(tài)方法
<span id="jumpId-then"></span>
Promise.prototype.then(onFulfilled, onRejected) 鏈式操作
-
Promise.prototype.then()
方法返回的是一個新的Promise對象
,因此可以采用鏈式寫法 -
Promise.prototype.then()
方法帶有以下三個參數(shù):成功回調(diào),失敗回調(diào),前進回調(diào),一般情況下只需要實現(xiàn)第一個,后面是可選的
function ptFir() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法1執(zhí)行');
resolve('方法1執(zhí)行完畢');
}, 2000)
})
}
function ptSec() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法2執(zhí)行');
resolve('方法2執(zhí)行完畢');
}, 1000)
})
}
function ptThi() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法3執(zhí)行');
resolve('方法3執(zhí)行完畢');
}, 3000)
})
}
ptFir().then(function(data) {
console.log('第一個回調(diào):' + data);
return ptSec();
}).then(function(data) {
console.log('第二個回調(diào):' + data);
return ptThi();
}).then(function(data) {
console.log('第三個回調(diào):' + data);
return '還沒完?該結(jié)束了吧!';
}).then(function(data) {
console.log(data);
})
<span id="jumpId-catch"></span>
Promise.prototype.catch(onRejected) 捕捉錯誤
-
Promise.prototype.catch()
方法是Promise.prototype.then(null, rejection)
的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù) -
Promise對象
的錯誤具有"冒泡"性質(zhì),會一直向后傳遞,直到被捕獲為止,也就是說,錯誤總是會被下一個catch
語句捕獲
promise().then(function(data) {
// todo something
}).catch(function(err) {
// 處理上個回調(diào)函數(shù)的錯誤
})
<span id="jumpId-finally"></span>
Promise.prototype.finally(onFinally) 最終操作
不管promise對象
最后的狀態(tài),在執(zhí)行完.then
或catch
指定的回調(diào)函數(shù)以后,都會執(zhí)行finally
方法指定的回調(diào)函數(shù)
promise().then(function(data) {
// todo something
}).catch(function(err) {
// 處理上個回調(diào)函數(shù)的錯誤
}).finally(function(result) {
// 處理then/catch之后必須執(zhí)行的操作
})
<span id="jumpId-resolve-reject"></span>
Promise.resolve() / Promise.reject()
- 這兩個是幫助方法或快捷方式,它們可以幫助您輕松創(chuàng)建
resolve
和reject
方法 - 需要注意的是:如果
Promise.resolve()
方法的參數(shù),
不是具有.then()
方法的對象(又稱thenable對象
),則返回一個新的Promise對象
,且它的狀態(tài)為fulfilled
;
如果Promise.resolve
方法的參數(shù)是一個Promise對象
的實例,則會被原封不動地返回 -
Promise.reject()
方法同上
var promise = Promise.resolve('Hello,這里是執(zhí)行成功的內(nèi)容!');
// var promise = Promise.reject('出錯了,這里是被發(fā)現(xiàn)的錯誤!'); // 成功和失敗只會有一種狀態(tài)哦
promise.then(function(data) {
console.log(data)
});
promise.catch(function(err) {
console.log(err)
});
<span id="jumpId-all"></span>
Promise.all()
- 當你處理多個
promise
時,最好先創(chuàng)建一個promise數(shù)組
,然后對這些promise集
執(zhí)行必要的操作 -
Promise.all(iterable) 方法
返回一個Promise實例
,此實例在iterable參數(shù)
內(nèi)所有的promise
- 都完成(resolved)或參數(shù)中不包含
promise
時回調(diào)完成(resolve) - 如果參數(shù)中
promise
有一個失敗(rejected),此實例回調(diào)失敗(reject),失敗原因的是第一個失敗promise
的結(jié)果 -
注意!!!這里的異步操作是并行執(zhí)行的,等到它們都執(zhí)行完后才會進到
then()
里面,
并且all()
方法會把所有異步操作的結(jié)果放進一個數(shù)組中傳給then()
-
下面的示例需要說明的內(nèi)容
- 只有3個方法的狀態(tài)都變成
fulfilled
,p的狀態(tài)才會變成fulfilled
,此時3個方法的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù) - 只要3個方法之中有一個被
rejected
,p的狀態(tài)就變成rejected
,此時第一個被reject
的實例的返回值,會傳遞給p的回調(diào)函數(shù)
- 只有3個方法的狀態(tài)都變成
function ptFir() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法1執(zhí)行');
resolve('方法1執(zhí)行完畢');
}, 2000)
})
}
function ptSec() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法2執(zhí)行');
resolve('方法2執(zhí)行完畢');
}, 1000)
})
}
function ptThi() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('方法3執(zhí)行');
resolve('方法3執(zhí)行完畢');
}, 3000)
})
}
Promise.all([ptFir(), ptSec(), ptThi()]).then(function(data) {
console.log(data);
console.log({}.toString.call(data));
})
<span id="jumpId-race"></span>
Promise.race()
-
Promise.race(iterable)
方法返回一個promise
,一旦迭代器中的某個promise
解決或拒絕,返回的promise
就會解決或拒絕 -
all()
方法的效果實際上是「誰跑的慢,以誰為準執(zhí)行回調(diào)」 ,
那么相對的就有另一個方法「誰跑的快,以誰為準執(zhí)行回調(diào)」,這就是race()
方法,這個詞本來就是賽跑的意思 - 這個
race()
方法有什么用呢?使用場景還是很多的,比如我們可以用race()
給某個異步請求設(shè)置超時時間,
并且在超時后執(zhí)行相應(yīng)的操作
// ptFir(), ptSec(), ptThi() 同上
Promise.all([ptFir(), ptSec(), ptThi()]).then(function(data) {
console.log(data);
console.log({}.toString.call(data));
})
使用Promise的一些經(jīng)驗法則
- 進行異步操作或使用阻塞代碼時,請使用
Promise
- 為了代碼的可讀性,
resolve()
方法對待.then()
,reject()
方法對應(yīng).catch()
- 確保同時寫入
.catch()
和.then()
方法來實現(xiàn)所有的promise
- 如果在這兩種情況下都需要做一些事情,請使用
.finally()
- 我們只有一次改變每個
promise (單一原則)
- 我們可以在一個
promise
中添加多個處理程序 -
Promise對象
中所有方法的返回類型,無論是靜態(tài)方法還是原型方法,都是Promise
- 在
all()
方法中,無論哪個promise
首先未完成,promise
的順序都保持在值變量中
參考文檔一 ———— 徹底理解Javascript 中的 Promise
參考文檔二 ———— 通俗淺顯的理解Promise中的then
參考文檔三 ———— 理解 Javascript 中的 Promise
參考文檔四 ———— JavaScript Promise 對象
我是 fx67ll.com,如果您發(fā)現(xiàn)本文有什么錯誤,歡迎在評論區(qū)討論指正,感謝您的閱讀!
如果您喜歡這篇文章,歡迎訪問我的 本文github倉庫地址,為我點一顆Star,Thanks~ :)
轉(zhuǎn)發(fā)請注明參考文章地址,非常感謝!!!