promise是什么腰鬼?
1、主要用于異步計(jì)算
2塑荒、可以將異步操作隊(duì)列化,按照期望的順序執(zhí)行姜挺,返回符合預(yù)期的結(jié)果
3齿税、可以在對(duì)象之間傳遞和操作promise,幫助我們處理隊(duì)列
為什么會(huì)有promise炊豪?
為了避免界面凍結(jié)(任務(wù))
同步:假設(shè)你去了一家飯店凌箕,找個(gè)位置拧篮,叫來(lái)服務(wù)員,這個(gè)時(shí)候服務(wù)員對(duì)你說(shuō)牵舱,對(duì)不起我是“同步”服務(wù)員串绩,我要服務(wù)完這張桌子才能招呼你。那桌客人明明已經(jīng)吃上了芜壁,你只是想要個(gè)菜單礁凡,這么小的動(dòng)作,服務(wù)員卻要你等到別人的一個(gè)大動(dòng)作完成之后慧妄,才能再來(lái)招呼你顷牌,這個(gè)便是同步的問(wèn)題:也就是“順序交付的工作1234,必須按照1234的順序完成”塞淹。
異步:則是將耗時(shí)很長(zhǎng)的A交付的工作交給系統(tǒng)之后窟蓝,就去繼續(xù)做B交付的工作,饱普。等到系統(tǒng)完成了前面的工作之后运挫,再通過(guò)回調(diào)或者事件,繼續(xù)做A剩下的工作套耕。
AB工作的完成順序谁帕,和交付他們的時(shí)間順序無(wú)關(guān),所以叫“異步”箍铲。
異步操作的常見(jiàn)語(yǔ)法
事件監(jiān)聽(tīng)
document.getElementById('#start').addEventListener('click',start,false);functionstart(){// 響應(yīng)事件雇卷,進(jìn)行相應(yīng)的操作}// jquery on 監(jiān)聽(tīng)$('#start').on('click',start)
回調(diào)
// 比較常見(jiàn)的有ajax$.ajax('http://www.wyunfei.com/',{success(res){// 這里可以監(jiān)聽(tīng)res返回的數(shù)據(jù)做回調(diào)邏輯的處理}})// 或者在頁(yè)面加載完畢后回調(diào)$(function(){// 頁(yè)面結(jié)構(gòu)加載完成,做回調(diào)邏輯處理})
有了nodeJS之后...對(duì)異步的依賴進(jìn)一步加劇了
大家都知道在nodeJS出來(lái)之前PHP颠猴、Java关划、python等后臺(tái)語(yǔ)言已經(jīng)很成熟了,nodejs要想能夠有自己的一片天翘瓮,那就得拿出點(diǎn)自己的絕活:
無(wú)阻塞高并發(fā)贮折,是nodeJS的招牌,要達(dá)到無(wú)阻塞高并發(fā)異步是其基本保障
舉例:查詢數(shù)據(jù)從數(shù)據(jù)庫(kù)资盅,PHP第一個(gè)任務(wù)查詢數(shù)據(jù)调榄,后面有了新任務(wù),那么后面任務(wù)會(huì)被掛起排隊(duì)呵扛;而nodeJS是第一個(gè)任務(wù)掛起交給數(shù)據(jù)庫(kù)去跑每庆,然后去接待第二個(gè)任務(wù)交給對(duì)應(yīng)的系統(tǒng)組件去處理掛起,接著去接待第三個(gè)任務(wù)...那這樣子的處理必然要依賴于異步操作
異步回調(diào)的問(wèn)題:
之前處理異步是通過(guò)純粹的回調(diào)函數(shù)的形式進(jìn)行處理
很容易進(jìn)入到回調(diào)地獄中今穿,剝奪了函數(shù)return的能力
問(wèn)題可以解決缤灵,但是難以讀懂,維護(hù)困難
稍有不慎就會(huì)踏入回調(diào)地獄 - 嵌套層次深,不好維護(hù)
回調(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)地獄的問(wèn)題
promise
promise是一個(gè)對(duì)象,對(duì)象和函數(shù)的區(qū)別就是對(duì)象可以保存狀態(tài)侣滩,函數(shù)不可以(閉包除外)
并未剝奪函數(shù)return的能力口注,因此無(wú)需層層傳遞callback,進(jìn)行回調(diào)獲取數(shù)據(jù)
代碼風(fēng)格君珠,容易理解寝志,便于維護(hù)
多個(gè)異步等待合并便于解決
promise詳解
newPromise(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产上、pending[待定]初始狀態(tài)
2棵磷、fulfilled[實(shí)現(xiàn)]操作成功
3、rejected[被否決]操作失敗
當(dāng)promise狀態(tài)發(fā)生改變晋涣,就會(huì)觸發(fā)then()里的響應(yīng)函數(shù)處理后續(xù)步驟仪媒;
promise狀態(tài)一經(jīng)改變,不會(huì)再變谢鹊。
Promise對(duì)象的狀態(tài)改變算吩,只有兩種可能:
從pending變?yōu)閒ulfilled
從pending變?yōu)閞ejected。
這兩種情況只要發(fā)生佃扼,狀態(tài)就凝固了偎巢,不會(huì)再變了。
最簡(jiǎn)單示例:
newPromise(resolve=>{setTimeout(()=>{resolve('hello')},2000)}).then(res=>{console.log(res)})
分兩次兼耀,順序執(zhí)行
newPromise(resolve=>{setTimeout(()=>{resolve('hello')},2000)}).then(val=>{console.log(val)//? 參數(shù)val = 'hello'returnnewPromise(resolve=>{setTimeout(()=>{resolve('world')},2000)})}).then(val=>{console.log(val)// 參數(shù)val = 'world'})
promise完成后then()
letpro=newPromise(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(失敗)
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í)行外面的
then嵌套
對(duì)于我們來(lái)說(shuō)侯嘀,此時(shí)最好將其展開(kāi),也是一樣的結(jié)果谱轨,而且會(huì)更好讀:
展開(kāi)增加可讀性
錯(cuò)誤處理
Promise會(huì)自動(dòng)捕獲內(nèi)部異常戒幔,并交給rejected響應(yīng)函數(shù)處理。
第一種錯(cuò)誤處理
第一種錯(cuò)誤處理
第二種錯(cuò)誤處理
第二種錯(cuò)誤處理
錯(cuò)誤處理兩種做法:
第一種:reject('錯(cuò)誤信息').then(() => {}, () => {錯(cuò)誤處理邏輯})
第二種:throw new Error('錯(cuò)誤信息').catch( () => {錯(cuò)誤處理邏輯})
推薦使用第二種方式碟嘴,更加清晰好讀溪食,并且可以捕獲前面所有的錯(cuò)誤(可以捕獲N個(gè)then回調(diào)錯(cuò)誤)
catch() + then()
第一種情況:
第一種情況
第一種情況 - 結(jié)果
結(jié)論:catch也會(huì)返回一個(gè)promise實(shí)例,并且是resolved狀態(tài)
第二種情況:
第二種情況
第二種情況結(jié)果
結(jié)論:拋出錯(cuò)誤變?yōu)閞ejected狀態(tài)娜扇,所以繞過(guò)兩個(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é)果
//切菜functioncutUp(){console.log('開(kāi)始切菜。');varp=newPromise(function(resolve,reject){//做一些異步操作setTimeout(function(){console.log('切菜完畢把沼!');resolve('切好的菜');},1000);});returnp;}//燒水functionboil(){console.log('開(kāi)始燒水啊易。');varp=newPromise(function(resolve,reject){//做一些異步操作setTimeout(function(){console.log('燒水完畢!');resolve('燒好的水');},1000);});returnp;}Promise.all([cutUp(),boil()]).then((result)=>{console.log('準(zhǔn)備工作完畢');console.log(result);})
Promise.race() 類似于Promise.all() 饮睬,區(qū)別在于它有任意一個(gè)完成就算完成
letp1=newPromise(resolve=>{setTimeout(()=>{resolve('I\`m p1 ')},1000)});letp2=newPromise(resolve=>{setTimeout(()=>{resolve('I\`m p2 ')},2000)});Promise.race([p1,p2]).then(value=>{console.log(value)})
常見(jiàn)用法:
異步操作和定時(shí)器放在一起租谈,,如果定時(shí)器先觸發(fā)捆愁,就認(rèn)為超時(shí)割去,告知用戶;
例如我們要從遠(yuǎn)程的服務(wù)家在資源如果5000ms還沒(méi)有加載過(guò)來(lái)我們就告知用戶加載失敗
現(xiàn)實(shí)中的用法
回調(diào)包裝成Promise昼丑,他有兩個(gè)顯而易見(jiàn)的好處:
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){letfindCityId=resCity.filter(item=>{if(item.id=='c1'){returnitem}})[0].id? ? ? ? ? $.ajax({//? 請(qǐng)求第二個(gè)API: 根據(jù)上一個(gè)返回的在北京公司的id “findCityId”,找到北京公司的第一家公司的idurl:'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',success(resPosition){letfindPostionId=resPosition.filter(item=>{if(item.cityId==findCityId){returnitem}})[0].id// 請(qǐng)求第三個(gè)API: 根據(jù)上一個(gè)API的id(findPostionId)找到具體公司胁附,然后返回公司詳情$.ajax({url:'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',success(resCom){letcomInfo=resCom.filter(item=>{if(findPostionId==item.id){returnitem}})[0]console.log(comInfo)}})}})}})
// Promise 寫(xiě)法// 第一步:獲取城市列表constcityList=newPromise((resolve,reject)=>{$.ajax({url:'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/city',success(res){resolve(res)}})})// 第二步:找到城市是北京的idcityList.then(res=>{letfindCityId=res.filter(item=>{if(item.id=='c1'){returnitem}})[0].idfindCompanyId().then(res=>{// 第三步(2):根據(jù)北京的id -> 找到北京公司的idletfindPostionId=res.filter(item=>{if(item.cityId==findCityId){returnitem}})[0].id// 第四步(2):傳入公司的idcompanyInfo(findPostionId)})})// 第三步(1):根據(jù)北京的id -> 找到北京公司的idfunctionfindCompanyId(){letaaa=newPromise((resolve,reject)=>{$.ajax({url:'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/position-list',success(res){resolve(res)}})})returnaaa}// 第四步:根據(jù)上一個(gè)API的id(findPostionId)找到具體公司酒繁,然后返回公司詳情functioncompanyInfo(id){letcompanyList=newPromise((resolve,reject)=>{$.ajax({url:'https://www.easy-mock.com/mock/5a52256ad408383e0e3868d7/lagou/company',success(res){letcomInfo=res.filter(item=>{if(id==item.id){returnitem}})[0]console.log(comInfo)}})})}
作者:王云飛_小四_wyunfei
鏈接:http://www.reibang.com/p/1b63a13c2701
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)控妻,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處州袒。