這篇文章主要為大家詳細介紹了Javascript創(chuàng)建Promise的方法,文中示例代碼介紹的非常詳細,零基礎(chǔ)也能參考此文章,感興趣的小伙伴們可以參考一下。
創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)整合營銷推廣、網(wǎng)站重做改版、臨漳網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5建站、商城建設(shè)、集團公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為臨漳等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。executor函數(shù)
我們知道,在創(chuàng)建一個Promise實例時,都會立即執(zhí)行executor函數(shù),executor函數(shù)傳遞兩個參數(shù),resolve和reject,如果executor函數(shù)執(zhí)行錯誤,Promise實例狀態(tài)會變?yōu)閞ejected
class MyPromise{ constructor(executor) { this.status = "pending"; // 初始化狀態(tài)為pending this.value = undefined; // 初始化返回的成功的結(jié)果或者失敗的原因 // 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回 let resolve = result => { if(this.status !== "pending") return; // 狀態(tài)一旦改變,就不會再變 this.status = "resolved"; this.value = result; } // 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回 let reject = reason => { if(this.status !== "pending") return; this.status = "rejected"; this.value = reason; } // try、catch捕獲異常,如果錯誤,執(zhí)行reject方法 try { executor(resolve, reject) } catch(err) { reject(err) } } }
我們來驗證一下,現(xiàn)在的Promise是什么樣的
let p1 = new MyPromise((resolve, reject) => { resolve(1); }) let p2 = new MyPromise((resolve, reject) => { reject(2); }) console.log(p1); console.log(p2);
可以看到,狀態(tài)已經(jīng)改變了,里面的值也是成功的結(jié)果和失敗的原因。then方法有兩個參數(shù),第一個參數(shù)是成功時執(zhí)行的,第二個參數(shù)為失敗后執(zhí)行的,then的鏈式調(diào)用和數(shù)組等是一樣的,每次執(zhí)行后會返回一個Promise實例。如果成功后,第一個then中成功的函數(shù)為null,它會繼續(xù)向下查找,直至不為null的函數(shù)執(zhí)行,上一個then中返回的結(jié)果會直接影響下一個then中執(zhí)行成功或者失敗的哪個函數(shù),了解了這些之后,我們嘗試實現(xiàn)一下~
then方法
then(resolveFn, rejectFn) { // 如果傳入的兩個參數(shù)不是函數(shù),則直接執(zhí)行返回結(jié)果 let resolveArr = []; let rejectArr = []; if(typeof resolveFn !== "function") { resolveFn = result => { return result; } } if(typeof rejectFn !== "function") { rejectFn = reason => { return MyPromise.reject(reason); } } return new Mypromise((resolve, reject) => { resolveArr.push(result => { try { let x = resolveFn(result); if(x instanceof MyPromise) { x.then(resolve, reject) return; } resolve(x); } catch(err) { reject(err) } }) rejectArr.push(reason => { try { let x = rejectFn(reason); if(x instanceof MyPromise) { x.then(resolve, reject) return; } resolve(x); } catch(err) { reject(err) } }) }) }
我們來整理一下上面的代碼
class MyPromise{ constructor(executor) { this.status = "pending"; // 初始化狀態(tài)為pending this.value = undefined; // 初始化返回的成功的結(jié)果或者失敗的原因 this.resolveArr = []; // 初始化then中成功的方法 this.rejectArr = []; // 初始化then中失敗的方法 // 定義change方法,因為我們發(fā)現(xiàn)好像resolve和reject方法共同的地方還挺多 let change = (status, value) => { if(this.status !== "pending") return; // 狀態(tài)一旦改變,就不會再變 this.status = status; this.value = value; // 根據(jù)狀態(tài)判斷要執(zhí)行成功的方法或失敗的方法 let fnArr = status === "resolved" ? this.resolveArr : this.rejectArr; // fnArr中的方法依次執(zhí)行 fnArr.forEach(item => { if(typeof item !== "function") return; item(this. value); }) } // 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回 let resolve = result => { change("resolved", result) } // 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回 let reject = reason => { change("rejected", reason); } // try、catch捕獲異常,如果錯誤,執(zhí)行reject方法 try { executor(resolve, reject) } catch(err) { reject(err) } } then(resolveFn, rejectFn) { // 如果傳入的兩個參數(shù)不是函數(shù),則直接執(zhí)行返回結(jié)果 if(typeof resolveFn !== "function") { resolveFn = result => { return result; } } if(typeof rejectFn !== "function") { rejectFn = reason => { return MyPromise.reject(reason); } } return new MyPromise((resolve, reject) => { this.resolveArr.push(result => { try { let x = resolveFn(result); // 獲取執(zhí)行成功方法返回的結(jié)果 // 如果x是一個promise實例,則繼續(xù)調(diào)用then方法 ==> then鏈的實現(xiàn) if(x instanceof MyPromise) { x.then(resolve, reject) return; } // 不是promise實例,直接執(zhí)行成功的方法 resolve(x); } catch(err) { reject(err) } }) this.rejectArr.push(reason => { try { let x = rejectFn(reason); if(x instanceof MyPromise) { x.then(resolve, reject) return; } resolve(x); } catch(err) { reject(err) } }) }) } }
我們來看一下效果
new MyPromise((resolve, reject) => { resolve(1); }).then(res => { console.log(res, 'success'); }, err => { console.log(err, 'error'); })
這時候,問題出現(xiàn)了,我們發(fā)現(xiàn)好像什么也沒有輸出,如果我們對上面的測試例子做一下小小的改動呢?
new MyPromise((resolve, reject) => { setTimeout(_ => { resolve(1); }, 0) }).then(res => { console.log(res, 'success'); // 1 "success" }, err => { console.log(err, 'error'); })
這是因為創(chuàng)建了Promise實例就立即執(zhí)行了executor函數(shù),還沒有執(zhí)行then方法,那么不管成功還是失敗的數(shù)組中,都是空的。那可能小伙伴們又有疑問了,為什么加了setTimeout就好使了呢?這是因為在事件隊列機制中,setTimeout會放入事件隊列中,等主線程執(zhí)行完成后再執(zhí)行,此時then方法會存儲成功或者失敗的函數(shù),所以不管是成功的數(shù)組還是失敗的數(shù)組中都已經(jīng)有值了,這個時候再去執(zhí)行就可以了。
但是我們不能在使用的時候?qū)憇etTimeout當做解決方案呀,既然我們在封裝,就要在封裝的函數(shù)內(nèi)解決問題,按照這樣的思路,我們也同樣可以在resolve和reject方法執(zhí)行的時候,判斷數(shù)組中是否有值,如果沒有,我們可以利用setTimeout讓它延后執(zhí)行,代碼如下:
// 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回 let resolve = result => { // 如果數(shù)組中有值,則立即改變狀態(tài) if(this.resolveArr.length > 0) { change("resolved", result) } // 如果沒值,則延后改變狀態(tài) let timer = setTimeout(_ => { change("resolved", result) clearTimeout(timer); }, 0) } // 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回 let reject = reason => { // 如果數(shù)組中有值,則立即改變狀態(tài) if(this.rejectArr.length > 0) { change("rejected", reason); } // 如果沒值,則延后改變狀態(tài) let timer = setTimeout(_ => { change("rejected", reason); clearTimeout(timer); }, 0) }
現(xiàn)在我們再試一下
// 1、已經(jīng)成功了 new MyPromise((resolve, reject) => { resolve('我成功啦,吼吼吼~~~~'); reject('我都已經(jīng)成功了,你別想讓我失敗,哼~~'); }).then(res => { console.log(res, 'success'); // 我成功啦,吼吼吼~~~~ success }, err => { console.log(err, 'error'); }) // 2、先失敗了 new MyPromise((resolve, reject) => { reject('失敗了,我好委屈,嗚嗚嗚~~'); resolve('已經(jīng)失敗了~~~'); }).then(res => { console.log(res, 'success'); }, err => { console.log(err, 'error'); // 失敗了,我好委屈,嗚嗚嗚~~ error }) // 3、鏈式調(diào)用 new MyPromise((resolve, reject) => { reject('失敗了,我好委屈,嗚嗚嗚~~'); resolve('已經(jīng)失敗了~~~'); }).then(res => { console.log(res); }, err => { console.log(err, 'error'); // 失敗了,我好委屈,嗚嗚嗚~~ error return '我要發(fā)奮圖強,不會被困難所擊倒,我要成功!??!' }).then(res1 => { console.log(res1, '經(jīng)過不懈努力,我終于在第二次成功了~'); // 我要發(fā)奮圖強,不會被困難所擊倒,我要成功?。?! 經(jīng)過不懈努力,我終于在第二次成功了~ }, err1 => { console.log(err1, '第二次失敗'); })
這就完美解決了第一次調(diào)用,不會執(zhí)行then方法的問題。同時,實現(xiàn)了鏈式的調(diào)用。對于鏈式的調(diào)用,其實不管是數(shù)組的鏈式調(diào)用,都是因為上一次返回的還是此實例。
catch方法
catch方法是捕獲異常,它和then方法的第二個回調(diào)函數(shù)是一樣的
catch(rejectFn) { return this.then(null, rejectFn) }
resolve方法
我們知道,Promsie也可以這樣用
let p1 = MyPromise.resolve(1); console.log(p1);
我們期望有這樣一種寫法,但是現(xiàn)在肯定會拋出錯誤:MyPromise.resolve不是一個方法
現(xiàn)在需要我們封裝一下resolve方法,我們需要明確的是,resolve之后,Promise是支持再繼續(xù)鏈式調(diào)用then的,所以,我們需要執(zhí)行resolve方法,返回一個Promise實例
static resolve(result) { // 返回新的promise實例,執(zhí)行promise實例中resolve方法 return new MyPromise(resolve => { resolve(result) }) }
reject方法
像resolve方法一樣,只不過它接收的是失敗的函數(shù)
static reject(reason) { // 返回新的promise實例,執(zhí)行promise實例中reject方法 return new MyPromise((_, reject) => { reject(reason); }) }
done方法
ES6標準入門一書中,對done方法的解釋是這樣的:無論Promise對象的回調(diào)鏈以then方法還是catch方法結(jié)尾,只要最后一個方法拋出錯誤,都有可能無法捕獲到。為此,Promise提供了一個done方法,它總是處于回掉鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤。
done(resolveFn, rejectFn) { this.then(resolveFn, rejectFn) .catch(reason => { setTimeout(() => { throw reason; }, 0) }) }
它可以接收fulfilled、rejected狀態(tài)的回調(diào)函數(shù),也可以不提供任何參數(shù)。但是無論怎樣,done方法都會捕捉到任何可能出現(xiàn)的錯誤,并向全局拋出
finally方法
finally方法是無論成功還是失敗都會執(zhí)行的方法,像這樣的方法還有小程序中的complete方法等等,我們來嘗試實現(xiàn)一下:
finally(finallyFn) { let P = this.constructor; return this.then( value => P.resolve(finallyFn()).then(() => value), reason => P.reject(finallyFn()).then(() => reason) ) }
我們來驗證一下
new MyPromise((resolve, reject) => { reject('失敗了,我好委屈,嗚嗚嗚~~'); resolve('已經(jīng)失敗了~~~'); }).then(res => { console.log(res); }, err => { console.log(err, 'error'); // 失敗了,我好委屈,嗚嗚嗚~~ error return '我要發(fā)奮圖強,不會被困難所擊倒,我要成功?。?!' }).finally(() => { console.log('執(zhí)行了嗎'); // 這里會輸出"執(zhí)行了嗎" })
all方法
all方法接收一個數(shù)組,當數(shù)組中每個實例都成功時才會返回,返回的也是一個數(shù)組,每個參數(shù)為對應(yīng)的promise返回的結(jié)果,如果有一項失敗了,all方法都會返回失敗
// 接收數(shù)組參數(shù) static all(promiseList) { // 返回新實例,調(diào)用后還可使用then、catch等方法 return new MyPromise((resolve, reject) => { let index = 0, // 成功次數(shù)計數(shù) results = []; // 返回的結(jié)果 for(let i = 0; i < promiseList.length; i++) { let item = promiseList[i]; // 如果item不是promise實例 if(!(item instanceof MyPromise)) return; item.then(result => { index++; results[i] = result; if(index === promiseList.length) { resolve(results); } }).catch(reason => { reject(reason); }) } }) }
來驗證一下
// 1.有失敗的情況 let p1 = MyPromise.resolve(1); let p2 = MyPromise.reject(2); let p3 = MyPromise.resolve(3); MyPromise.all([p1, p2, p3]) .then(res => { console.log(res); }).catch(err => { console.log(err, 'err'); // 2 "err" }) // 2.無失敗的情況 let p1 = MyPromise.resolve(1); let p2 = MyPromise.resolve(2); let p3 = MyPromise.resolve(3); MyPromise.all([p1, p2, p3]) .then(res => { console.log(res, 'success'); // [1, 2, 3] "success" }).catch(err => { console.log(err, 'err'); })
race方法
race方法同樣接收一個數(shù)組參數(shù),里面每一項是Promise實例,它返回最快改變狀態(tài)的Promise實例方法的結(jié)果
static race(promiseList) { return new MyPromise((resolve, reject) => { promiseList.forEach(item => { if(!(item instanceof MyPromise)) return; item.then(result => { resolve(result); }).catch(err => { reject(err) }) }) }) } 復(fù)制代碼驗證 // 1. let p1 = MyPromise.resolve(1); let p2 = MyPromise.reject(2); let p3 = MyPromise.resolve(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res); // 1 'success' }).catch(err => { console.log(err, 'err'); }) // 2. let p1 = MyPromise.reject(1); let p2 = MyPromise.resolve(2); let p3 = MyPromise.resolve(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res, 'success'); }).catch(err => { console.log(err, 'err'); // 1 'err' }) // 3. let p1 = MyPromise.reject(1); let p2 = MyPromise.reject(2); let p3 = MyPromise.reject(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res, 'success'); }).catch(err => { console.log(err, 'err'); // 1 'err' })
嘗試實現(xiàn)allSettled方法
allSettled方法也是接收數(shù)組參數(shù),但是它無論成功或者失敗,都會返回
static allSettled(promiseList) { return new MyPromise((resolve, reject) => { let results = []; for(let i = 0; i < promiseList.length; i++) { let item = promiseList[i]; if(!(item instanceof MyPromise)) return; item.then(result => { results[i] = result; }, reason => { results[i] = reason; }) resolve(results); } }) } 復(fù)制代碼驗證 // 1. let p1 = MyPromise.resolve(1); let p2 = MyPromise.resolve(2); let p3 = MyPromise.resolve(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res); // [1, 2, 3] 'success' }).catch(err => { console.log(err, 'err'); }) // 2. let p1 = MyPromise.reject(1); let p2 = MyPromise.reject(2); let p3 = MyPromise.reject(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res, 'success'); // [1, 2, 3] 'success' }).catch(err => { console.log(err, 'err'); }) // 3. let p1 = MyPromise.resolve(1); let p2 = MyPromise.reject(2); let p3 = MyPromise.resolve(3); MyPromise.race([p1, p2, p3]) .then(res => { console.log(res, 'success'); // [1, 2, 3] 'success' }).catch(err => { console.log(err, 'err'); })
以上就是Javascript創(chuàng)建Promise的方法,代碼詳細清楚,如果在日常工作遇到這個問題,希望你能通過這篇文章解決問題。如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!