JavaScript實(shí)現(xiàn)Promise流程詳解
構(gòu)造函數(shù)
首先我們來看一下我們是如何使用promise的,我們在實(shí)例化對象是這么使用的:
let p1 = new Promise((resolve, reject) => { let random = Math.floor(Math.random() * 10); if (random > 4) { resolve('sucess') } else { reject('erro') } })
所以我們在創(chuàng)建我們自己的類要考慮到如何使用這個(gè)參數(shù)。
我們來看一下, new Promise 的時(shí)候傳了一個(gè)回調(diào)函數(shù),在這個(gè)回調(diào)函數(shù)中的代碼應(yīng)該是被立即執(zhí)行的。
而在這個(gè)回調(diào)函數(shù)中,還帶有這兩個(gè)參數(shù)resolve和reject(也是回調(diào)函數(shù))。
所以在我們的構(gòu)造函數(shù)中,應(yīng)該是有這兩個(gè)函數(shù)resolve和reject(暫時(shí)先不管這兩個(gè)函數(shù)是做什么的)。
我們知道promise是有三個(gè)屬性的:
pending : 待定
fulfilled : 對應(yīng)resolve函數(shù)
rejected : 對應(yīng)reject函數(shù)
并且狀態(tài)一旦改變就不能再更改了。
所以我們的構(gòu)造函數(shù)之中應(yīng)該有表示當(dāng)前promise狀態(tài)的屬性。
我們知道不管使用resolve還是reject都會傳入一個(gè)res變量,作為結(jié)果值,所以我們在用一個(gè)屬性來保存resolve和reject的結(jié)果值。
最后我們可以設(shè)計(jì)出這樣的構(gòu)造函數(shù):
function Mypromise (config) { this.status = 'pending'; this.res = '' let resolve = (data) => { this.status = 'fulfilled'; this.res = data } let reject = (data) => { this.status = 'rejected'; this.res = data } config(resolve, reject) }
then 和 catch方法
我們先來回顧一哈怎么使用這兩個(gè)方法:
p1 .then(res => { console.log(res); }) .then(res => { console.log(res); }) .catch(err => { console.log(err); })
上面的代碼我們可以看到,then和catch方法,都接受了一個(gè)回調(diào)函數(shù)
而這個(gè)回調(diào)函數(shù)的參數(shù)也就是我們之前定義的this.res。
所以我們可以想到這么做:
Mypromise.prototype.then = function (config) { if (this.status == 'fulfilled') { config(this.res) } } Mypromise.prototype.catch = function (config) { if (this.status == 'rejected') { config(this.res) } }
但是這種方法不能實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,就是不能連著使用then方法。
但是如果我想實(shí)現(xiàn)出這個(gè)模式,我們應(yīng)該在then方法下回一個(gè)對象,而這個(gè)對象正常來講就是this。
所以我們可以直接返回this嗎,看下面這個(gè)情況。
p1 .then(res => { console.log(res); return new Promise((resolve, reject) => { resolve('1111') }) }) .then(res => { console.log(res); }) .catch(err => { console.log(err); })
在then方法下如果返回了一個(gè)新的promise的話,我們就不能直接在then方法里面直接返回this了。
所以我們應(yīng)該先判斷then的回調(diào)函數(shù)是否返回了新的對象,如果沒有才返回當(dāng)前then的this對象。
Mypromise.prototype.then = function (config) { if (this.status == 'fulfilled') { var res = config(this.res) } return res || this; } Mypromise.prototype.catch = function (config) { if (this.status == 'rejected') { var res = config(this.res) } return res || this; }
解決異步問題
上面的代碼,似乎看著沒有什么問題了,但是如果我這么寫的話:
let p2 = new Mypromise((resolve, reject) => { setTimeout(() => { reject('p2 resolve') }, 1000); })
問題就大大的出來了,為什么呢? 因?yàn)槲以趐2.then的時(shí)候,定時(shí)器沒有跑完,所以p2的狀態(tài)現(xiàn)在還是pending,根本不會走下去。
這里面我們用一種經(jīng)典的解決模式,在我寫之前的axios和路由也經(jīng)??梢钥吹?。
在then方法中,如果當(dāng)前狀態(tài)為pending(這句話很重要o),我們就把當(dāng)前的回調(diào)函數(shù)保存下來(不一定是一個(gè),有可能是多個(gè)then,所以我們采用數(shù)組保存)。
那我們保存起來什么時(shí)候用呢?當(dāng)然是在定時(shí)器結(jié)束后用!那定時(shí)器什么時(shí)候結(jié)束呢?當(dāng)然是當(dāng)前promise狀態(tài)改變的時(shí)候,所以,我們在resolve和reject方法之中,要將這些方法進(jìn)行調(diào)用?。?!
所以我們要修改構(gòu)造函數(shù):
function Mypromise (config) { this.status = 'pending'; this.res = ''; this.saveResolve = []; this.saveReject = []; let resolve = (data) => { if (this.status == 'pending') { this.status = 'fulfilled'; this.res = data this.saveResolve.forEach(val => { val(this.res) }) } } let reject = (data) => { if (this.status == 'pending') { this.status = 'rejected'; this.res = data this.saveReject.forEach(val => { val(this.res) }) } } config(resolve, reject); }
然后再修改我們的then和catch方法:
Mypromise.prototype.then = function (config) { if (this.status == 'pending') { this.saveResolve.push(config); } if (this.status == 'fulfilled') { var res = config(this.res) } return res || this; } Mypromise.prototype.catch = function (config) { if (this.status == 'pending') { this.saveReject.push(config) } if (this.status == 'rejected') { var res = config(this.res) } return res || this; }
這樣關(guān)于異步的問題我們就解決了。
all和race方法
還是老樣子,在寫之前我們先回顧一下是怎么用的:
Mypromise.all([p2, p3, p4]) .then(res => { console.log(res); }) .catch(err => { console.log(err); }) Mypromise.race([p2, p3, p4]) .then(res => { console.log(res); }) .catch(err => { console.log(err); })
那我們知道,二者都死以一個(gè)數(shù)組作為參數(shù),這里面我門就不考慮其他的情況了,我就當(dāng)數(shù)組里面全是promise對象了。。。
二者的區(qū)別在于:
all:當(dāng)所有的promise都執(zhí)行完,并且狀態(tài)都為fulfilled,all方法返回的promise為fulfilled,否則為rejected。
race:第一個(gè)出現(xiàn)結(jié)果的promise對象就是race放回的promise的結(jié)果。
現(xiàn)在我們來想一下all方法如何來實(shí)現(xiàn),我們拿到了數(shù)組參數(shù)之后,一定是要遍歷一遍的。
然后對于每一個(gè)元素都調(diào)用then方法和catch方法。
then方法要有一個(gè)結(jié)果數(shù)組保存每個(gè)promise的結(jié)果值。
我們可以用一個(gè)計(jì)數(shù)器來計(jì)算then方法的調(diào)用次數(shù),如果計(jì)數(shù)器的大小等于數(shù)組長度,那么就證明所有的promise全部都是fulfilled,可以返回結(jié)果數(shù)組。
catch方法只要是被調(diào)用了一次,那么直接返回結(jié)果,不多bb,直接返回
最后記住要把新的promise返回o。
Mypromise.all = function (arr) { let result = []; let count = 0; let promise = new Mypromise((resolve, reject) => { for (var i = 0; i < arr.length; i++) { arr[i] .then(res => { result.push(res); count++; if (count == arr.length) resolve(result); }) .catch(err => { reject(err) }) } }) return promise }
race的方法的話,實(shí)現(xiàn)起來可能就更簡單了,不管那個(gè)promise的then方法還是catch方法觸發(fā)了,直接返回結(jié)果:
Mypromise.race = function (arr) { let promise = new Mypromise((resolve, reject) => { for (var i = 0; i < arr.length; i++) { arr[i] .then(res => { resolve(res); }) .catch(err => { reject(err) }) } }) return promise }
到此這篇關(guān)于JavaScript實(shí)現(xiàn)Promise流程詳解的文章就介紹到這了,更多相關(guān)JS Promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Ant Design Blazor 組件庫的路由復(fù)用多標(biāo)簽頁功能
在 Ant Design Blazor 組件庫中實(shí)現(xiàn)多標(biāo)簽頁組件的呼聲日益高漲。于是,我利用周末時(shí)間,結(jié)合 Blazor 內(nèi)置路由組件實(shí)現(xiàn)了基于 Tabs 組件的 ReuseTabs 組件,需要的朋友跟隨小編一起看看吧2021-07-07javascript實(shí)現(xiàn)顯示和隱藏div方法匯總
本文章通過幾個(gè)簡單的實(shí)例告訴你如何來實(shí)例關(guān)于隱藏與顯示div層及關(guān)閉層與隱藏的區(qū)別分析哦,有需要的同學(xué)可以參考一下本文章。2015-08-08JavaScript資源預(yù)加載組件和滑屏組件的使用推薦
這篇文章主要介紹了JavaScript資源預(yù)加載組件和滑屏組件的使用推薦,分別為preload和slide的用法講解,使用起來非常簡單,需要的朋友可以參考下2016-03-03JavaScript實(shí)現(xiàn)限時(shí)秒殺功能
各種電商活動都喜換選擇限時(shí)秒殺活動形式,這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)限時(shí)秒殺功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08