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

