什么是 promise?它的作用是什么?

首先說一下promise 是什么?

1、本質(zhì)是構(gòu)造函數(shù)中主要用于異步計(jì)算

2、可以將異步操作隊(duì)列化,按照期望的順序執(zhí)行,返回符合預(yù)期的結(jié)果

其次主要處理異步和操作同步化

promise 有三個(gè)狀態(tài):

1、pending[待定]初始狀態(tài)

2、fulfilled[實(shí)現(xiàn)]操作成功

3、rejected[被否決]操作失敗

最后我們通過new 創(chuàng)建promise,而實(shí)例化之后的promise對(duì)象的參數(shù)是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)有兩個(gè)參數(shù),分別為成功回調(diào) resolve 和失敗回調(diào) reject ,通過原型上 then() 方法來接收成功之后的數(shù)據(jù)

異步操作的常見語法

????????let str =?new?Promise(function(resolve,?reject)?{

????????????//做一些異步操作

????????????setTimeout(function()?{

????????????????console.log('執(zhí)行完成Promise');

????????????????resolve("");? ?//? 要返回的數(shù)據(jù)可以任何數(shù)據(jù)例如接口返回?cái)?shù)據(jù)

????????????},?2000);

????????})

console.log(str);

事件監(jiān)聽

document.getElementById('#start').addEventListener('click', start, false);

function start() {

? // 響應(yīng)事件,進(jìn)行相應(yīng)的操作

}

// jquery on 監(jiān)聽

$('#start').on('click', start)

回調(diào)

// 比較常見的有ajax

$.ajax('http://www.wyunfei.com/', {

success (res) {

? // 這里可以監(jiān)聽res返回的數(shù)據(jù)做回調(diào)邏輯的處理

}

})

// 或者在頁面加載完畢后回調(diào)

$(function() {

// 頁面結(jié)構(gòu)加載完成,做回調(diào)邏輯處理

})


異步回調(diào)的問題:

之前處理異步是通過純粹的回調(diào)函數(shù)的形式進(jìn)行處理

很容易進(jìn)入到回調(diào)地獄中,剝奪了函數(shù)return的能力

問題可以解決,但是難以讀懂,維護(hù)困難

稍有不慎就會(huì)踏入回調(diào)地獄 - 嵌套層次深,不好維護(hù)

need-to-insert-img

回調(diào)地獄

一般情況我們一次性調(diào)用API就可以完成請(qǐng)求。

有些情況需要多次調(diào)用服務(wù)器API,就會(huì)形成一個(gè)鏈?zhǔn)秸{(diào)用,比如為了完成一個(gè)功能,我們需要調(diào)用API1、API2、API3,依次按照順序進(jìn)行調(diào)用,這個(gè)時(shí)候就會(huì)出現(xiàn)回調(diào)地獄的問題

promise

promise是一個(gè)對(duì)象,對(duì)象和函數(shù)的區(qū)別就是對(duì)象可以保存狀態(tài),函數(shù)不可以(閉包除外)

并未剝奪函數(shù)return的能力,因此無需層層傳遞callback,進(jìn)行回調(diào)獲取數(shù)據(jù)

代碼風(fēng)格,容易理解,便于維護(hù)

多個(gè)異步等待合并便于解決

promise詳解

new Promise(

? function (resolve, reject) {

? ? // 一段耗時(shí)的異步操作

? ? resolve('成功') // 數(shù)據(jù)處理完成

? ? // reject('失敗') // 數(shù)據(jù)處理出錯(cuò)

? }

).then(

? (res) => {console.log(res)},? // 成功

? (err) => {console.log(err)} // 失敗

)

resolve作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;

reject作用是,將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。

promise有三個(gè)狀態(tài):

1、當(dāng)promise狀態(tài)發(fā)生改變,就會(huì)觸發(fā)then()里的響應(yīng)函數(shù)處理后續(xù)步驟;

2、promise狀態(tài)一經(jīng)改變,不會(huì)再變。

3、Promise對(duì)象的狀態(tài)改變,只有兩種可能:

? ? ? 從pending變?yōu)閒ulfilled

? ? ? 從pending變?yōu)閞ejected。

這兩種情況只要發(fā)生,狀態(tài)就凝固了,不會(huì)再變了。

最簡(jiǎn)單示例:

new Promise(resolve => {

? setTimeout(() => {

? ? resolve('jianyan')

? }, 2000)

}).then(res => {

? console.log(res)

})

分兩次,順序執(zhí)行

new Promise(resolve => {

? ? setTimeout(() => {

? ? ? resolve('jianyan')

? ? }, 2000)

? }).then(val => {

? ? console.log(val) //? 參數(shù)val = 'hello'

? ? return new Promise(resolve => {

? ? ? setTimeout(() => {

? ? ? ? resolve('mochen')

? ? ? }, 2000)

? ? })

? }).then(val => {

? ? console.log(val) // 參數(shù)val = 'world'

? })

promise完成后then()

let pro = new Promise(resolve => {

? setTimeout(() => {

? ? resolve('hello world')

? }, 2000)

})

setTimeout(() => {

? pro.then(value => {

? console.log(value) // hello world

})

}, 2000)

結(jié)論:promise作為隊(duì)列最為重要的特性,我們?cè)谌魏我粋€(gè)地方生成了一個(gè)promise隊(duì)列之后,我們可以把他作為一個(gè)變量傳遞到其他地方。

假如在.then()的函數(shù)里面不返回新的promise,會(huì)怎樣?

.then()

1、接收兩個(gè)函數(shù)作為參數(shù),分別代表fulfilled(成功)和rejected(失?。?/p>

2、.then()返回一個(gè)新的Promise實(shí)例,所以它可以鏈?zhǔn)秸{(diào)用

3、當(dāng)前面的Promise狀態(tài)改變時(shí),.then()根據(jù)其最終狀態(tài),選擇特定的狀態(tài)響應(yīng)函數(shù)執(zhí)行

4、狀態(tài)響應(yīng)函數(shù)可以返回新的promise,或其他值,不返回值也可以我們可以認(rèn)為它返回了一個(gè)null;

5、如果返回新的promise,那么下一級(jí).then()會(huì)在新的promise狀態(tài)改變之后執(zhí)行

6、如果返回其他任何值,則會(huì)立即執(zhí)行下一級(jí).then()

.then()里面有.then()的情況

1、因?yàn)?then()返回的還是Promise實(shí)例

2、會(huì)等里面的then()執(zhí)行完,再執(zhí)行外面的

need-to-insert-img

then嵌套

對(duì)于我們來說,此時(shí)最好將其展開,也是一樣的結(jié)果,而且會(huì)更好讀:

need-to-insert-img

展開增加可讀性

錯(cuò)誤處理

Promise會(huì)自動(dòng)捕獲內(nèi)部異常,并交給rejected響應(yīng)函數(shù)處理。

第一種錯(cuò)誤處理

need-to-insert-img

第一種錯(cuò)誤處理

第二種錯(cuò)誤處理

need-to-insert-img

第二種錯(cuò)誤處理

錯(cuò)誤處理兩種做法:

第一種:reject('錯(cuò)誤信息').then(() => {}, () => {錯(cuò)誤處理邏輯})

第二種:throw new Error('錯(cuò)誤信息').catch( () => {錯(cuò)誤處理邏輯})

推薦使用第二種方式,更加清晰好讀,并且可以捕獲前面所有的錯(cuò)誤(可以捕獲N個(gè)then回調(diào)錯(cuò)誤)

catch() + then()

第一種情況:

need-to-insert-img

第一種情況

need-to-insert-img

第一種情況 - 結(jié)果

結(jié)論:catch也會(huì)返回一個(gè)promise實(shí)例,并且是resolved狀態(tài)

第二種情況:

need-to-insert-img

第二種情況

need-to-insert-img

第二種情況結(jié)果

結(jié)論:拋出錯(cuò)誤變?yōu)閞ejected狀態(tài),所以繞過兩個(gè)then直接跑到最下面的catch

Promise.all() 批量執(zhí)行

Promise.all([p1, p2, p3])用于將多個(gè)promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例,返回的實(shí)例就是普通的promise

它接收一個(gè)數(shù)組作為參數(shù)

數(shù)組里可以是Promise對(duì)象,也可以是別的值,只有Promise會(huì)等待狀態(tài)改變

當(dāng)所有的子Promise都完成,該P(yáng)romise完成,返回值是全部值得數(shù)組

有任何一個(gè)失敗,該P(yáng)romise失敗,返回值是第一個(gè)失敗的子Promise結(jié)果

//切菜

? ? function cutUp(){

? ? ? ? console.log('開始切菜。');

? ? ? ? var p = new Promise(function(resolve, reject){? ? ? ? //做一些異步操作

? ? ? ? ? ? setTimeout(function(){

? ? ? ? ? ? ? ? console.log('切菜完畢!');

? ? ? ? ? ? ? ? resolve('切好的菜');

? ? ? ? ? ? }, 1000);

? ? ? ? });

? ? ? ? return p;

? ? }

? ? //燒水

? ? function boil(){

? ? ? ? console.log('開始燒水。');

? ? ? ? var p = new Promise(function(resolve, reject){? ? ? ? //做一些異步操作

? ? ? ? ? ? setTimeout(function(){

? ? ? ? ? ? ? ? console.log('燒水完畢!');

? ? ? ? ? ? ? ? resolve('燒好的水');

? ? ? ? ? ? }, 1000);

? ? ? ? });

? ? ? ? return p;

? ? }

? ? Promise.all([cutUp(), boil()])

? ? ? ? .then((result) => {

? ? ? ? ? ? console.log('準(zhǔn)備工作完畢');

? ? ? ? ? ? console.log(result);

? ? ? ? })

Promise.race() 類似于Promise.all() ,區(qū)別在于它有任意一個(gè)完成就算完成

let p1 = new Promise(resolve => {

? ? ? ? setTimeout(() => {

? ? ? ? ? ? resolve('I\`m p1 ')

? ? ? ? }, 1000)

? ? });

? ? let p2 = new Promise(resolve => {

? ? ? ? setTimeout(() => {

? ? ? ? ? ? resolve('I\`m p2 ')

? ? ? ? }, 2000)

? ? });

? ? Promise.race([p1, p2])

? ? ? ? .then(value => {

? ? ? ? ? ? console.log(value)

? ? ? ? })

常見用法:

異步操作和定時(shí)器放在一起,,如果定時(shí)器先觸發(fā),就認(rèn)為超時(shí),告知用戶;

例如我們要從遠(yuǎn)程的服務(wù)家在資源如果5000ms還沒有加載過來我們就告知用戶加載失敗

現(xiàn)實(shí)中的用法

回調(diào)包裝成Promise,他有兩個(gè)顯而易見的好處:

1、可讀性好

2、返回 的結(jié)果可以加入任何Promise隊(duì)列

實(shí)戰(zhàn)示例,回調(diào)地獄和promise對(duì)比:

/***

? 第一步:找到北京的id

? 第二步:根據(jù)北京的id -> 找到北京公司的id

? 第三步:根據(jù)北京公司的id -> 找到北京公司的詳情

? 目的:模擬鏈?zhǔn)秸{(diào)用、回調(diào)地獄

***/

// 回調(diào)地獄

// 請(qǐng)求第一個(gè)API: 地址在北京的公司的id

$.ajax({

? url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',

? success (resCity) {

? ? let findCityId = resCity.filter(item => {

? ? ? if (item.id == 'c1') {

? ? ? ? return item

? ? ? }

? ? })[0].id

? ? $.ajax({

? ? ? //? 請(qǐng)求第二個(gè)API: 根據(jù)上一個(gè)返回的在北京公司的id “findCityId”,找到北京公司的第一家公司的id

? ? ? url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',

? ? ? success (resPosition) {

? ? ? ? let findPostionId = resPosition.filter(item => {

? ? ? ? ? if(item.cityId == findCityId) {

? ? ? ? ? ? return item

? ? ? ? ? }

? ? ? ? })[0].id

? ? ? ? // 請(qǐng)求第三個(gè)API: 根據(jù)上一個(gè)API的id(findPostionId)找到具體公司,然后返回公司詳情

? ? ? ? $.ajax({

? ? ? ? ? url: 'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',

? ? ? ? ? success (resCom) {

? ? ? ? ? ? let comInfo = resCom.filter(item => {

? ? ? ? ? ? ? if (findPostionId == item.id) {

? ? ? ? ? ? ? ? return item

? ? ? ? ? ? ? }

? ? ? ? ? ? })[0]

? ? ? ? ? ? console.log(comInfo)

? ? ? ? ? }

? ? ? ? })

? ? ? }

? ? })

? }

})

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容