Promise 鏈式調(diào)用原理精簡示例
前言
在面試的過程中,總有一些面試官會問你,手寫一個簡易版的Promise
得行不,得行的話就寫一個出來看看,啪一哈,就把紙和筆給了你。 我們思索半天就寫出來了個下面這個。 哦豁,高薪張開了它的翅膀,遠離了我們。
class Promise { constructor (resolve, reject) {} resolve () {} reject (){} then () {} catch () {} once () {} all () {} ... }
本篇文章將不講述手寫出來一個簡易的Promise
,感興趣的朋友可以去看我這篇文章 -> Promise詳解-手寫Promise,實現(xiàn)一款自己的簡易Promise
本篇文章記錄的是如何實現(xiàn)Promise
的核心功能之一的.then 鏈式調(diào)用,采用構(gòu)造函數(shù)的寫法,本篇文章的代碼不考慮任何容錯和異常處理,只單獨說明其鏈式調(diào)用原理,方便理解。
先擺上完整代碼,去掉注釋和一些換行共20行有余。
代碼
function CustomPromise (fn) { // 回調(diào)收集 this.callbackList = [] // 傳遞給Promise處理函數(shù)的resolve const resolve = (value) => { // 注意promise的then函數(shù)需要異步執(zhí)行 setTimeout(() => { // 這里直接往實例上掛個data this.data = value; // 把callbackList數(shù)組里的函數(shù)依次執(zhí)行一遍 this.callbackList.forEach(cb => cb(value)) }); } /* 執(zhí)行用戶傳入的函數(shù) 并且把resolve方法交給用戶執(zhí)行 */ fn(resolve) } /* 重點 */ // 往構(gòu)造函數(shù)的原型上掛載.then方法 CustomPromise.prototype.then = function (onReaolved) { // return 一個promise 實例 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{ const response = onReaolved(this.data) // 判斷是否是一個 CustomPromise if(response instanceof CustomPromise){ // resolve 的權(quán)力被交給了user promise response.then(resolve) }else{ // 如果是普通值,直接resolve // 依次執(zhí)行callbackList里的函數(shù) 并且把值傳遞給callbackList resolve(response) } }) }) }
經(jīng)典案例
new CustomPromise((resolve) => { setTimeout(() => { // resolve1 resolve(1); }, 300); }).then((res) => {// then1 console.log(res); // 返回一個 CustomPromise return new CustomPromise((resolve) => { setTimeout(() => { // resolve2 resolve(2); }, 300); }); }).then(res => {// then2 console.log(res); });
完整的代碼和例子已奉上,現(xiàn)在來進行解釋。 固然結(jié)果很重要,但過程也很重要。我們要做到 知其然知其所以然。
解析
第一步
首先,我們我們先創(chuàng)建這樣一個Promise
, 這里需要使用匿名函數(shù),不能使用箭頭函數(shù),或者你可以根據(jù)這個方法已class
類的方法進行實現(xiàn)。
大概步驟如下:
- 聲明構(gòu)造函數(shù)/類
- 在內(nèi)部聲明一個數(shù)組名為
callbackList
用來裝回調(diào),并放到this
里面 - 聲明一個名
resolve
的方法,用來傳遞給Promise
進行處理,注意:resolve 內(nèi)部需要為異步,這里可以采用 setTimeout 實現(xiàn) - 循環(huán)
callbackList
并執(zhí)行里面的方法
寫出來后的樣子長這樣:
function CustomPromise (fn) { // 回調(diào)收集 this.callbackList = [] // 傳遞給Promise處理函數(shù)的resolve const resolve = (value) => { // 注意promise的then函數(shù)需要異步執(zhí)行 setTimeout(() => { // 這里直接往實例上掛個data this.data = value; // 把callbackList數(shù)組里的函數(shù)依次執(zhí)行一遍 this.callbackList.forEach(cb => cb(value)) }); } /* - fn 為用戶傳進來的函數(shù) - 執(zhí)行用戶傳入的函數(shù) - 并且把resolve方法交給用戶執(zhí)行 */ fn(resolve) }
第二步
注意:第二步是本篇文章的重點,也是這個核心功能的一個重點。
我們需要往CustomPromise
的原型上掛載一個.then
的方法。并返回的是一個Promise
實例,這里依舊使用的是匿名函數(shù)。
完整代碼長這樣:
// 往構(gòu)造函數(shù)的原型上掛載.then方法 CustomPromise.prototype.then = function (onReaolved) { // return 一個promise 實例 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{ const response = onReaolved(this.data) // 判斷是否是一個 CustomPromise if(response instanceof CustomPromise){ // resolve 的權(quán)力被交給了user promise response.then(resolve) }else{ // 如果是普通值,直接resolve // 依次執(zhí)行callbackList里的函數(shù) 并且把值傳遞給callbackList resolve(response) } }) }) }
寫出來過后,在結(jié)合上面的那個例子使用,不能說和原生Promise
一模一樣,但使用起來的鏈式效果卻是一毛一樣。
分析說明,此過程需結(jié)合上文中的案例一起閱讀
const promise1 = new CustomPromise((resolve) => { setTimeout(() => resolve(1)); }) promise1.then((res) => { const userPromise = new CustomPromise((resolve) => { setTimeout(() => resolve(2), 300); }); return userPromise });
說明:
- 我們把
new Promise
返回的實例叫做promise1
- 在
Promise.prototype.then
的實現(xiàn)中,我們構(gòu)造了一個新的promise
返回,叫它promise2
在調(diào)用then
方法的時候,用戶手動構(gòu)造了一個promise
并且返回,用來做異步的操作,叫它userPromise
,那么在then
的實現(xiàn)中,內(nèi)部的this
其實就指向promise1
而promise2
的傳入的fn
函數(shù)執(zhí)行了一個this.cbs.push()
的操作,其實是往promise1
的callbackList
數(shù)組中push
了一個函數(shù),等待后續(xù)執(zhí)行
CustomPromise.prototype.then = function (onReaolved) { // promise 2 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{}) }) }
如果用戶傳入給then
的onResolved
方法返回的是個userPromise
,那么這個userPromise
里用戶會自己去在合適的時機 resolvePromise2
,那么進而這里的response.then(resolve)
中的resolve
就會被執(zhí)行
if(response instanceof CustomPromise){ response.then(resolve) }
再結(jié)合上面的經(jīng)典案例看,我這里再放一遍
new CustomPromise((resolve) => { setTimeout(() => { // resolve1 resolve(1); }, 300); }).then((res) => {// then1 console.log(res); // userPromise return new CustomPromise((resolve) => { setTimeout(() => { // resolve2 resolve(2); }, 300); }); }).then(res => {// then2 console.log(res); });
then1
這一整塊其實返回的是promise2
,那么then2
其實本質(zhì)上是promise2.then(()=>{})
, 也就是說then2
注冊的回調(diào)函數(shù),其實進入了promise2
的callbackList
回調(diào)數(shù)組里。 又因為我們剛剛知道,resolve2
調(diào)用了之后,userPromise
會被resolve
,進而觸發(fā)promise2
被resolve
,進而 promise2
里的callbackList
數(shù)組被依次觸發(fā)。 這樣就實現(xiàn)了用戶自己寫的resolve2
執(zhí)行完畢后,then2
里的邏輯才會繼續(xù)執(zhí)行,也就是異步鏈式調(diào)用。
說句題外話,這個有點繞,當時還是看了好一會才看懂。
好了,當你看到這里的時候,這篇文章已經(jīng)接近尾聲了,是時候進行總結(jié)了。
總結(jié)
本篇文章只是根據(jù)其原理實現(xiàn)的一個簡易鏈式調(diào)用的過程,真正的Promise
并沒有這么簡單,和上文中的比起來復雜很多,而且涉及到很多的異常、容錯、邊界等情況的處理。
最后推薦一下Promise A+規(guī)范
-> 點我查看規(guī)范,很值得去看,相信看完后會對Promise
有一個更深的了解。
以上就是Promise 鏈式調(diào)用原理精簡示例的詳細內(nèi)容,更多關(guān)于Promise 鏈式調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript實現(xiàn)點擊單元格改變背景色的方法
這篇文章主要介紹了JavaScript實現(xiàn)點擊單元格改變背景色的方法,涉及JavaScript響應鼠標事件動態(tài)操作頁面元素屬性的相關(guān)技巧,需要的朋友可以參考下2016-02-02純JavaScript實現(xiàn)的兼容各瀏覽器的添加和移除事件封裝
這篇文章主要介紹了純JavaScript實現(xiàn)的兼容各瀏覽器的添加和移除事件封裝,本文直接給出實現(xiàn)代碼,代碼中帶詳細注釋,需要的朋友可以參考下2015-03-03javascript實現(xiàn)檢驗的各種規(guī)則
這篇文章主要介紹了javascript實現(xiàn)檢驗的各種規(guī)則,涉及javascript針對手機號、郵箱、網(wǎng)址、漢字及圖片等相關(guān)檢測技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07[Bootstrap-插件使用]Jcrop+fileinput組合實現(xiàn)頭像上傳功能實例代碼
這篇文章主要介紹了[Bootstrap-插件使用]Jcrop+fileinput組合實現(xiàn)頭像上傳功能實例代碼,非常具有實用價值,需要的朋友可以參考下。2016-12-12用JavaScript實現(xiàn)仿Windows關(guān)機效果
用JavaScript實現(xiàn)仿Windows關(guān)機效果...2007-03-03JavaScript實現(xiàn)頁面定時刷新(定時器,meta)
很多朋友看到定時,很容易想到用js定時器,還有盆友用meta來設(shè)置,下面小編給大家介紹js實現(xiàn)頁面定時刷新的方法,一起看看吧2016-10-10