如何利用ES6進行Promise封裝總結(jié)
原生Promise解析
簡介
promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案--回調(diào)函數(shù)和事件--更合理和強大。
promise簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,從語法上來說,Promise是一個對象,從它可以獲取異步操作的消息,Promise提供統(tǒng)一的API,各種異步操作都可以用同樣的方法進行處理
特點
對象的狀態(tài)不受外界影響,Promise對象代表一個異步操作,有三種狀態(tài):Pendding、fulfilled、rejected。只有異步操作的結(jié)果,可以決定當前是哪一種狀態(tài),其他操作都無法改變這個狀態(tài)。
一旦狀態(tài)改變,就不會在變,任何時候都可以得到這個結(jié)果,只有兩種可能:從Pendding變?yōu)閒ulfilled和從Pendding變?yōu)閞ejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,會一直保持這個結(jié)果,這時就稱為resolved。
利用es6進行Promise封裝
處理同步任務(wù)
原生方法調(diào)用方式
new Promise((resolve,reject)=>{ resolve(1) }).then(res=>{ console.log(res) //1 })
同步封裝思考
1.由調(diào)用方式可見Promise是一個類
2.它接收一個回調(diào)函數(shù),這個回調(diào)函數(shù)接受resolve和reject方法作為參數(shù)
3.當狀態(tài)改變后執(zhí)行then方法,并將resolve或reject的結(jié)果作為then方法接受回調(diào)函數(shù)的參數(shù)
class Mypromise{ constructor(callback){ this.status='pendding' //成功結(jié)果 this.s_res = null // 失敗結(jié)果 this.f_res = null callback((arg)=>{ // 使用箭頭函數(shù)this不會丟失 // 改變狀態(tài)為成功 this.status = 'fulfilled' this.s_res = arg },(arg)=>{ // 改變狀態(tài)為失敗 this.status = 'rejected' this.f_res = arg }) } then(onresolve,onreject){ if(this.status === 'fulfilled'){ // 當狀態(tài)為成功時 onresolve(this.s_res) }else if(this.status === 'rejected'){ // 當狀態(tài)為失敗時 onreject(this.f_res) } } }
處理異步任務(wù)
原生調(diào)用方式
new Promise((resolve,reject)=>{ setTimeOut(()=>{ resolve(1) },1000) }).then(res=>{ console.log(res) })
異步封裝思考
1.根據(jù)js執(zhí)行機制,setTimeOut屬于宏任務(wù),then回調(diào)函數(shù)屬于微任務(wù),當主線程執(zhí)行完成后,會從異步隊列中取出本次的微任務(wù)先執(zhí)行。
2.也就是說,then方法執(zhí)行時,狀態(tài)還沒有改變,所有我們需要將then方法執(zhí)行的回調(diào)保存起來,等到異步代碼執(zhí)行完成后,在統(tǒng)一執(zhí)行then方法的回調(diào)函數(shù)
class Mypromise{ constructor(callback){ this.status='pendding' //成功結(jié)果 this.s_res = null // 失敗結(jié)果 this.f_res = null this.query = [] // ++ callback((arg)=>{ // 使用箭頭函數(shù)this不會丟失 // 改變狀態(tài)為成功 this.status = 'fulfilled' this.s_res = arg // 當狀態(tài)改變后,統(tǒng)一執(zhí)行then方法的回調(diào) this.query.forEach(item=>{ item.resolve(arg) }) },(arg)=>{ // 改變狀態(tài)為失敗 this.status = 'rejected' this.f_res = arg // 當狀態(tài)改變后,統(tǒng)一執(zhí)行then方法的回調(diào) this.query.forEach(item=>{ item.reject(arg) }) }) } then(onresolve,onreject){ if(this.status === 'fulfilled'){ // 當狀態(tài)為成功時 onresolve(this.s_res) }else if(this.status === 'rejected'){ // 當狀態(tài)為失敗時 onreject(this.f_res) }else{ // ++ 狀態(tài)沒有改變 this.query.push({ // 保存回調(diào)函數(shù)到隊列中 resolve:onresolve, reject:onreject }) } } }
處理鏈式調(diào)用
原生調(diào)用方式
new Promise((resolve,reject)=>{ resolve(1) }).then(res=>{ return res }).then(res=>{ console.log(res) })
鏈式調(diào)用思考
原生的Promise對象的then方法,返回的也是一個Promise對象,一個新的Promise才能支持鏈式調(diào)用
下一個then方法可以接受上一個then方法的返回值作為回調(diào)函數(shù)的參數(shù)
主要考慮上一個then方法的返回值:
1.Promise對象/具有then方法的對象
2.其他值
第一個then方法返回一個Promise對象,它的回調(diào)函數(shù)接受resFn和rejFN兩個回調(diào)函數(shù)作為參數(shù),把成功狀態(tài)的處理封裝為handle函數(shù),接受成功的結(jié)果作為參數(shù)
在handle函數(shù),根據(jù)onresolve返回值的不同做出不同的處理
class Mypromise{ constructor(callback){ this.status='pendding' //成功結(jié)果 this.s_res = null // 失敗結(jié)果 this.f_res = null this.query = [] // ++ callback((arg)=>{ // 使用箭頭函數(shù)this不會丟失 // 改變狀態(tài)為成功 this.status = 'fulfilled' this.s_res = arg // 當狀態(tài)改變后,統(tǒng)一執(zhí)行then方法的回調(diào) this.query.forEach(item=>{ item.resolve(arg) }) },(arg)=>{ // 改變狀態(tài)為失敗 this.status = 'rejected' this.f_res = arg // 當狀態(tài)改變后,統(tǒng)一執(zhí)行then方法的回調(diào) this.query.forEach(item=>{ item.reject(arg) }) }) } then(onresolve,onreject){ return new Mypromise((resFN,rejFN)=>{ if(this.status === 'fulfilled'){ // 當狀態(tài)為成功時 handle(this.s_res) }else if(this.status === 'rejected'){ // 當狀態(tài)為失敗時 errBack(this.f_res) }else{ // ++ 狀態(tài)沒有改變 this.query.push({ // 保存回調(diào)函數(shù)到隊列中 resolve:onresolve, reject:onreject }) } function handle(value){ // 當then方法的onresolve方法有返回值時,保存其返回值,沒有使用其保存的值 let returnVal = onresolve instanceof Function && onresolve(value) || value // 如果onresolve方法返回的是promise對象,則調(diào)用其then方法 if(returnVal&&returnVal['then'] instanceof Function){ returnVal.then(res=>{ resFN(res) },err=>{ rejFN(err) }) }else{ resFN(returnVal) } } function errBack(reason){ if(onreject instanceof Function){ let returnVal = reject(reason) if(typeof returnVal !== 'undenfined' && returnVal['then'] instanceof Function){ returnVal.then(res=>{ resFN(res) },err=>{ rejFN(err) }) }else{ resFN(returnVal) } }else{ rejFN(reason) } } }) } }
Promise.all和Promise.race方法
原生調(diào)用方式
Promise.all方法接受一個數(shù)組,數(shù)組中的每一項都是一個Promise實例,只有數(shù)組中的所有Promise實例的狀態(tài)都變?yōu)閒ulfilled時,此時整個狀態(tài)才會變成fulfilled,此時數(shù)組中所有Promise實例的返回值組成一個新的數(shù)組,進行傳遞。
Promise.race方法和Promise.all方法一樣,如果不是Promise實例,就會先調(diào)用Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise實例,在進行下一步處理。
只要數(shù)組中有一個參數(shù)的狀態(tài)變?yōu)閒ulfilled就會進行傳遞
// 將現(xiàn)有對象轉(zhuǎn)換為Promise對象 Mypromise.resolve = (arg)=>{ if(typeof arg == 'undefined' || arg==null){ // 不帶有任何參數(shù) return new Mypromise(resolve=>{ resolve(arg) }) }else if(arg instanceof Mypromise){ // 是一個Mypromise實例 return arg }else if(arg['then'] instanceof Function){ // 具有then方法的對象 return new Mypromise((resolve,reject)=>{ arg.then(res=>{ resolve(res) },err=>{ reject(err) }) }) }else{ // 參數(shù)不是具有then方法的對象,或根本不是對象 return new Mypromise(resolve=>{ resolve(arg) }) } } Mypromise.all = (arr)=>{ if(!Array.isArray(arr)){ throw new TypeError('參數(shù)必須是一個數(shù)組') } return new Mypromise((resolve,reject)=>{ let i=0,result=[] next() functon next(){ // 如果不是Mypromise實例需要轉(zhuǎn)換 Mypromise.resolve(arr[i]).then(res=>{ result.push(res) i++ if(i===arr.length){ resolve(result) }else{ next() } },reject) } }) } Mypromise.race = (arr)=>{ if(!Array.isArray(arr)){ throw new TypeError('參數(shù)必須是一個數(shù)組') } return new Mypromise((resolve,reject)=>{ let done = false arr.forEach(item=>{ Mypromise.resolve(item).then(res=>{ if(!done){ resolve(res) done = true } },err=>{ if(!done){ reject(res) done = true } }) }) }) }
處理Mypromise狀態(tài)確定不能改變的特性
在重寫callback中的resolve和reject方法執(zhí)行前,先判斷狀態(tài)是否為'pendding'
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Echarts實現(xiàn)點擊列表聯(lián)動餅圖的示例代碼
本文主要介紹了Echarts實現(xiàn)點擊列表聯(lián)動餅圖的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05javascript 使用sleep函數(shù)的常見方法詳解
這篇文章主要介紹了javascript 使用sleep函數(shù)的常見方法,結(jié)合實例形式分析總結(jié)了javascript sleep函數(shù)的功能、常見使用方法與操作注意事項,需要的朋友可以參考下2020-04-04淺談MUI框架中加載外部網(wǎng)頁或服務(wù)器數(shù)據(jù)的方法
下面小編就為大家分享一篇淺談MUI框架中加載外部網(wǎng)頁或服務(wù)器數(shù)據(jù)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01- 水平不高,不能也不想從太深的層次去講解這個東西,只是根據(jù)一段比較有代表性的代碼,結(jié)合執(zhí)行結(jié)果,從表象上粗淺地談?wù)劇?/div> 2010-12-12
最新評論