如何做出 JS Promise 從頭開始_第1頁
如何做出 JS Promise 從頭開始_第2頁
如何做出 JS Promise 從頭開始_第3頁
如何做出 JS Promise 從頭開始_第4頁
如何做出 JS Promise 從頭開始_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

?今天,我們創(chuàng)建自己的JavaScriptPromise實(shí)現(xiàn)[FromScratch]。要?jiǎng)?chuàng)建一個(gè)新的承諾,我們只需newPromise像這樣使用:

newPromise((resolve,reject)=>{

...

resolve(someValue)

})我們傳遞一個(gè)定義Promise特定行為的回調(diào)。Promise是一個(gè)容器:為我們提供API來管理和轉(zhuǎn)換價(jià)值這讓我們能夠管理和轉(zhuǎn)變實(shí)際上尚不存在的價(jià)值。使用容器來包裝值是函數(shù)式編程范例中的常見做法。函數(shù)式編程中有不同種類的“容器”。最著名的是函子和單子。實(shí)施承諾以了解其內(nèi)部結(jié)構(gòu)1、then()方法classPromise{

constructor(then)

{

this.then=then

}}constgetItems=newPromise((resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(body)

})})getItems.then(renderItems,console.error)非常簡(jiǎn)單,到目前為止,這個(gè)實(shí)現(xiàn)除了具有success(

resolve)和error(

reject)回調(diào)的任何函數(shù)之外,沒有做任何其他事情。因此,檢查一下,當(dāng)我們從頭開始做出承諾時(shí),我們有一個(gè)額外的(通常不公開的)步驟需要實(shí)施。2.映射目前,我們的Promise實(shí)現(xiàn)無法正常工作-它過于簡(jiǎn)化,并且不包含正常工作所需的所有行為。我們的實(shí)現(xiàn)目前缺少什么功能和/或行為之一?首先,我們無法鏈?zhǔn)?then()調(diào)用。Promise可以鏈接多個(gè).then()方法,并且每次.then()解析其中任何語句的結(jié)果時(shí)都應(yīng)返回一個(gè)新的Promise。這是讓Promise如此強(qiáng)大的主要功能之一。它們幫助我們逃離回調(diào)地獄。這也是我們目前尚未實(shí)現(xiàn)的Promise實(shí)現(xiàn)的一部分。在我們的實(shí)現(xiàn)中,結(jié)合使Promise鏈正常工作所需的所有功能可能會(huì)有點(diǎn)混亂-但我們得到了這一點(diǎn)。讓我們深入研究、簡(jiǎn)化并設(shè)置JavaScriptPromise的實(shí)現(xiàn),以始終從語句返回或解析其他Promise

.then()。首先,我們需要一個(gè)方法來轉(zhuǎn)換Promise包含的值并返回一個(gè)新的Promise。嗯,這聽起來是不是很奇怪?讓我們仔細(xì)看看。啊哈,這聽起來和Atotype.map實(shí)現(xiàn)方式完全一樣,不是嗎?.map的類型簽名是:map::(a->b)->Arraya->Arrayb簡(jiǎn)單來說,這意味著map接受一個(gè)函數(shù)并將type轉(zhuǎn)換a為type

b。這可以是一個(gè)String到Boolean,然后它將采用a

(字符串)的數(shù)組并返回b

(布爾)的數(shù)組。我們可以構(gòu)建一個(gè)Ptotype.map具有非常相似簽名的函數(shù),該函數(shù)Atotype.map允許我們將已解決的Promise結(jié)果映射到另一個(gè)正在進(jìn)行的Promise。這就是我們能夠鏈接.then's具有返回任何隨機(jī)結(jié)果的回調(diào)函數(shù)的方式,但隨后似乎神奇地以某種方式返回Promise,而無需我們實(shí)例化任何新的Promise。map::(a->b)->Promisea->Promiseb以下是我們?nèi)绾卧谀缓髮?shí)現(xiàn)這一魔法:classPromise{

constructor(then)

{

this.then=then

}

map(mapper)

{

returnnewPromise(

(resolve,reject)=>

this.then(x=>resolve(mapper(x)),

reject

)

)

}}我們剛才做了什么?讓我們來分解一下。當(dāng)我們創(chuàng)建或?qū)嵗疨romise時(shí),我們定義了一個(gè)回調(diào),它是我們成功解析結(jié)果時(shí)使用的then回調(diào)。我們創(chuàng)建一個(gè)接受映射器函數(shù)的映射函數(shù)。這個(gè)映射函數(shù)返回一個(gè)新的承諾。在返回新的Promise之前,它會(huì)嘗試解析先前Promise使用的結(jié)果。我們將map先前Promise的結(jié)果轉(zhuǎn)換為新的Promise,然后回到在我們的map方法中實(shí)例化的新創(chuàng)建的Promise的范圍內(nèi)。.then我們可以繼續(xù)這種模式,根據(jù)需要附加盡可能多的回調(diào),并始終返回一個(gè)新的Promise,而無需在我們的map方法之外外部實(shí)例化任何新的Promise。(resolve,reject)=>this.then(...))正在發(fā)生的事情是我們this.then立即打電話。thethis指的是我們當(dāng)前的Promise,因此this.then將為我們提供Promise當(dāng)前的內(nèi)部值,或者如果我們的Promise失敗則給出當(dāng)前的錯(cuò)誤。我們現(xiàn)在需要給它一個(gè)resolve回調(diào)reject://nextresolve=x=>resolve(mapper(x))//nextreject=reject這是我們的地圖功能中最重要的部分。首先,我們用mapper當(dāng)前值填充我們的函數(shù)x:promise.map(x=>x+1)//Themapperisactuallyx=>x+1//sowhenwedomapper(10)//itreturns11.我們直接將這個(gè)新值(11在示例中)傳遞給resolve我們正在創(chuàng)建的新Promise的函數(shù)。如果Promise被拒絕,我們只需傳遞新的拒絕方法,而不對(duì)值進(jìn)行任何修改。

map(mapper){

returnnewPromise((resolve,reject)=>this.then(

x=>resolve(mapper(x)),

reject

))

}constpromise=newPromise((resolve,reject)=>{

setTimeout(()=>resolve(10),1000)})promise

.map(x=>x+1)//=>Promise(11)

.then(x=>console.log(x),err=>console.error(err))//=>it'sgoingtolog'11'總而言之,我們?cè)谶@里所做的事情非常簡(jiǎn)單。我們只是用映射器函數(shù)和下一個(gè)函數(shù)的組合resolve來覆蓋我們的函數(shù)。

這會(huì)將我們的值傳遞給映射器并解析返回的值。resolvex多使用我們的Promise實(shí)現(xiàn):constgetItems=newPromise((resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(body)

})})getItems

.map(JSON.parse)

.map(json=>json.data)

.map(items=>items.filter(isEven))

.map(items=>items.sort(priceAsc))

.then(renderPrices,console.error)就像這樣,我們就被束縛了。我們鏈接的每個(gè)回調(diào)都是一個(gè)有點(diǎn)死的簡(jiǎn)單函數(shù)。這就是為什么我們喜歡在函數(shù)式編程中進(jìn)行柯里化?,F(xiàn)在我們可以編寫以下代碼:getItems

.map(JSON.parse)

.map(prop('data'))

.map(filter(isEven))

.map(sort(priceAsc))

.then(renderPrices,console.error)可以說,鑒于您更熟悉函數(shù)語法,您可以說這段代碼更干凈。另一方面,如果您不熟悉函數(shù)語法,那么這段代碼會(huì)變得非?;靵y。因此,為了更好地理解我們正在做的事情,讓我們明確定義我們的.then()方法在每次調(diào)用時(shí)將如何轉(zhuǎn)換.map:步驟1:newPromise((resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(body)

})})第2步:.then現(xiàn)在:then=(resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(body)

})}

.map(JSON.parse).then就是現(xiàn)在:then=(resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(JSON.parse(body))

})}步驟3:

.map(x=>x.data).then就是現(xiàn)在:then=(resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(JSON.parse(body).data)

})}步驟4:

.map(items=>items.filter(isEven)).then就是現(xiàn)在:then=(resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(JSON.parse(body).data.filter(isEven))

})}第6步:

.map(items=>items.sort(priceAsc)).then就是現(xiàn)在:then=(resolve,reject)=>{

HTTP.get('/items',(err,body)=>{

if(err)returnreject(err)

resolve(JSON.parse(body).data.filter(isEven).sort(priceAsc))

})}第6步:

.then(renderPrices,console.error).then叫做。我們執(zhí)行的代碼如下所示:HTTP.get('/items',(err,body)=>{

if(err)returnconsole.error(err)

renderMales(JSON.parse(body).data.filter(isEven).sort(priceAsc))})3.鏈接和flatMap()我們的Promise實(shí)現(xiàn)仍然缺少一些東西——鏈接。當(dāng)您在方法內(nèi)返回另一個(gè)Promise時(shí).then,它??會(huì)等待它解析并將解析的值傳遞給下一個(gè).then內(nèi)部函數(shù)。這個(gè)工作怎么樣?在一個(gè)Promise中,.then也在壓扁這個(gè)promise容器。數(shù)組類比為flatMap:[1,2,3,4,5].map(x=>[x,x+1])//=>[[1,2],[2,3],[3,4],[4,5],[5,6]][1,2,3,4,5].flatMap(x=>[x,x+1])//=>[1,2,2,3,3,4,4,5,5,6]getPerson.flatMap(person=>getFriends(person))//=>Promise(Promise([Person]))getPerson.flatMap(person=>getFriends(person))//=>Promise([Person])這是我們的簽名細(xì)分,但如果很難理解,我建議您嘗試多次追蹤邏輯尾部,如果它沒有點(diǎn)擊,則嘗試深入研究下面的直接實(shí)現(xiàn)。我們非常深入,并且沒有函數(shù)式編程的經(jīng)驗(yàn),這種語法可能很難跟蹤,但請(qǐng)盡力而為,讓我們繼續(xù)下面的內(nèi)容。classPromise{

constructor(then)

{

this.then=then

}

map(mapper)

{

returnnewPromise(

(resolve,reject)=>this.then(

x=>resolve(mapper(x)),

reject

)

)

}

flatMap(mapper){

returnnewPromise(

(resolve,reject)=>this.then(

x=>mapper(x).then(resolve,reject),

reject

)

)

}}我們知道的flatMap映射器函數(shù)將返回一個(gè)Promise。當(dāng)我們得到值x時(shí),我們調(diào)用映射器,然后通過調(diào)用.then返回的Promise來轉(zhuǎn)發(fā)我們的resolve和reject函數(shù)。getPerson

.map(JSON.parse)

.map(x=>x.data)

.flatMap(person=>getFriends(person))

.map(json=>json.data)

.map(friends=>friends.filter(isMale))

.map(friends=>friends.sort(ageAsc))

.then(renderMaleFriends,console.error)怎么樣:)我們實(shí)際上通過分離Promise的不同行為來創(chuàng)建一個(gè)Monad。簡(jiǎn)單地說,monad是一個(gè)容器,它實(shí)現(xiàn)了具有以下類型簽名的.map方法.flatMap:map::(a->b)->Monada->MonadbflatMap::(a->Monadb)->Monada->Monadb該flatMap方法也稱為chain或bind。我們剛剛構(gòu)建的實(shí)際上稱為任務(wù),方法.then通常命名為fork。classTask{

constructor(fork)

{

this.fork=fork

}

map(mapper)

{

returnnewTask((resolve,reject)=>this.fork(

x=>resolve(mapper(x)),

reject

))

}

chain(mapper)

{

returnnewTask((resolve,reject)=>this.fork(

x=>mapper(x).fork(resolve,reject),

reject

))

}}Task和Promise之間的主要區(qū)別在于

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論