作者:杜珂珂 (滬江前端開發(fā)工程師)
本文為原創(chuàng)文章,有不當(dāng)之處歡迎指出。轉(zhuǎn)載請標(biāo)明出處掰派。
一個(gè)新事物的產(chǎn)生必然是有其歷史原因的。為了更好的以同步的方式寫異步的代碼左痢,人們在JS上操碎了心靡羡。從Deferred到Promise到Generator再到Async和Await,各種工具的涌現(xiàn)就像是人類文明的進(jìn)化史俊性,從無到有略步,從簡陋到完善。
在這其中磅废,Promise是較為重要的一個(gè)工具纳像,也是后面不同工具進(jìn)化完善的基石,今天我們就來了解一下:我們?yōu)槭裁葱枰狿romise以及Promise為我們解決了什么問題拯勉。
我們需要什么竟趾?
1. 回調(diào)地獄
我們應(yīng)該都遇到過這樣的代碼:
callback嵌套函數(shù)層數(shù)少了還好,如果更多的話宫峦,大量的縮進(jìn)會(huì)你的代碼慢慢地向屏幕右側(cè)移動(dòng)岔帽。如果你寫了幾十層回調(diào),那么你的代碼在一個(gè)屏幕的寬度上可能放不下了导绷。這會(huì)讓我們的代碼變得難以維護(hù)犀勒,像一坨*。誠然我們可以通過其它的方式,把代碼抽出來平級(jí)對待贾费,例如:
這么做雖然好看了钦购,但: 1. 切斷了我們代碼結(jié)構(gòu)上的層級(jí)串聯(lián),而是采用了非結(jié)構(gòu)的指向串聯(lián) 褂萧,跳來跳去的函數(shù)引用押桃,很可能就把我們繞暈了 2. 要耗費(fèi)更多的時(shí)間來整理代碼
我們想要的是:既能串起來寫,又能少花時(shí)間导犹。而Promise恰恰解決了這么一個(gè)痛點(diǎn)唱凯。
var promise = new Promise(...);
promise.then(...)
.then(...)
.then(...)
.then(...)
.catch(...)
對!我們其實(shí)要的就是這樣一種“語法糖”谎痢。
2. 不讓回調(diào)里的return和throw變成擺設(shè)
想想你在$.ajax()的success回調(diào)里寫個(gè)return xxx磕昼,沒啥用,return給誰节猿?這種感覺就像打了一記空拳票从。另外,你如果在異步回調(diào)里throw一個(gè)錯(cuò)誤滨嘱,外面想接一下纫骑,比如你想這樣:
恭喜你!接不到九孩,error被扔到全局環(huán)境里去了,BOOM发框!然后主線程就掛了躺彬。
Promise解決方案是什么?
首先在字面意思上看Promise梅惯,翻譯成“承諾”的話有些抽象宪拥。那么具象一點(diǎn),我們可以把Promise看成某種“合約”的管理者和執(zhí)行者铣减。
對她君,我們可以先把“他”想象成一個(gè)人,給他起一個(gè)簡短的名字葫哗,叫做:“P先生”缔刹。下面,就來看一下劣针,“P先生”是如何幫助我們解決問題的(由于解決方案是“P先生”提供的校镐,所以我們需要按照“他”的套路來)。
1. ?定下合約
首先捺典,我們需要按照某個(gè)模板的樣子寫下一個(gè)“合約”鸟廓,“合約”里告訴“P先生”的是將來可能發(fā)生的事情,“P先生”針對我們和“他”約定的事項(xiàng),做不同的處理和反饋引谜。Promise只對未來發(fā)生的事情做出兩種基本情況的應(yīng)對:“成功” 和 “失敗”牍陌。
上述代碼中,首先我們召喚(new)了一個(gè)“P先生”员咽,然后在回調(diào)函數(shù)里告訴了“他”具體的合約內(nèi)容毒涧,合約規(guī)定:在setTimeout異步操作中,如果得到的秒數(shù)大于30s骏融,則合約生效并且完成链嘀;否則的話,合約無效并且完成档玻』巢矗“P先生”點(diǎn)點(diǎn)頭說:“好的,沒問題”误趴。
注意:一旦做完resolve或者reject操作霹琼,合約的狀態(tài)就算定死了,沒辦法再改動(dòng)了凉当。
2. 合約完成后的操作
上一步做完枣申,“P先生”只會(huì)告訴我們合約完成的結(jié)果:成功 或 失敗。那么成功后該怎么做看杭?失敗后該怎么做忠藤?我們還沒有告訴“P先生”,下面揮一揮小手(引用promise變量)楼雹,把“P先生”招過來模孩,繼續(xù)囑咐:
then給出了應(yīng)對成功或失敗具體需要的方案,里面需要傳兩個(gè)處理函數(shù)
如果then中只傳遞了onRejected函數(shù)的話贮缅,那么相當(dāng)于catch榨咐。
看到這里,我們發(fā)現(xiàn)Promise一定程度上幫我們解決了錯(cuò)誤捕獲的問題谴供。上述例子块茁,我們在then鏈中產(chǎn)生的錯(cuò)誤,最后都能被catch捕捉到(前提是你在最后面寫了catch)桂肌。
then里面的函數(shù)該寫點(diǎn)什么数焊?
每一個(gè) promise 都會(huì)提供給你一個(gè) then()函數(shù)。當(dāng)我們的代碼跑在 then()函數(shù)內(nèi)部時(shí)轴或,是這樣子的:
在這個(gè)函數(shù)內(nèi)部我們可以做什么呢昌跌?
也就是說我可以這么寫:
在上面的例子中看到,我們能在then中愉快的使用return和throw照雁,并順利將值傳遞給下一層then或catch來處理蚕愤。到這里Promise其實(shí)已經(jīng)解決了之前我們說過的問題:“在異步回調(diào)里不能有效的使用return和throw”
總結(jié)
看到這里答恶,想必各位看官應(yīng)該已經(jīng)清楚的認(rèn)識(shí)到Promise是個(gè)什么東西,并幫助我們解決了什么問題吧萍诱?當(dāng)然悬嗓,Promise API還提供了很多的方法,比如race,?all裕坊,reject,?resolve包竹。本文的主要目的還是為了解釋Promise產(chǎn)生的原因和“他”解決了什么問題,至于API上的其它方法籍凝,相信在理解Promise的基礎(chǔ)上周瞎,會(huì)很容易就掌握的。
在異步“轉(zhuǎn)”同步這條道路上饵蒂,Promise只是一個(gè)出彩的點(diǎn)声诸,“他”還尚有一些缺陷和不足,并不是我們最終的解決方案退盯。下一次彼乌,我們將一起學(xué)習(xí)新的工具:Generator!
參考資料:
【翻譯】We have a problem with promises
ES6 JavaScript Promise的感性認(rèn)知
如果喜歡本文渊迁,可以訂閱滬江技術(shù)學(xué)院公眾號(hào)慰照。