摘要:說白了,就是給聲明的添加一個包含空的對象,再由函數(shù)返回這個空。如此構(gòu)成一個層層包裹的鏈。四首先本質(zhì)是一個遞歸函數(shù),結(jié)束條件是,即終止到未掛載對象的子為止。可以看到這個函數(shù)的作用就是根據(jù)屬性逐個觸發(fā)鏈中的或函數(shù)。
背景
Promise是一種非常實用的異步編程方案,本文對于Promise源碼的分析可以增進讀者對于Promise原理的理解,以后不再害怕異步編程,不用擔心callback hell,對于Promise的運用也將更加游刃有余;另外對于源碼的閱讀也是對自己代碼能力提升的一種有效手段,本文會從實踐到原理將Promise進行一次徹底的解剖,希望會對讀者有所助益。
文章組織安排本文會分四個部分進行介紹
第一部分:Promise鏈式觸發(fā)原理,及resolve(), reject(),then()的實現(xiàn)
第二部分:promise的在實踐中的幾個特性及其實現(xiàn)
第三部分:promise的幾個主要的類方法和對象方法的實現(xiàn)
第四部分:promise錯誤處理機制
var promiseObj = new Promise(function(resolve, reject){ setTimeout(function(){ resolve("to then1"); }, 1000); }); promiseObj.then(function(val){ console.log(val); return "to then2"; }).then(function(val){ console.log(val); });
以上是一段簡單的promise處理異步操作的代碼,初始化中有一個包含setTimeout異步操作的函數(shù),在兩個then中定義了兩個異步操作后的執(zhí)行函數(shù);promise的作用是實現(xiàn)在異步操作完成之后去觸發(fā)then中聲明的函數(shù),以取代之前不斷定義callback的方式,將異步操作通過同步的寫法實現(xiàn),使得程序書寫符合開放封閉原則;整個的實現(xiàn)過程分這么幾步:
promise實例化Promise.prototype.then()創(chuàng)建promise鏈
resolve遞歸觸發(fā)promise鏈
以下是接下來將用到的幾個工具函數(shù),其作用已經(jīng)在注釋中說明
//聲明一個空函數(shù),用于初始化一個沒有執(zhí)行邏輯的promise var noop = function(){}; var IS_ERROR = {}; var LAST_ERROR = null; //將a作為fn的參數(shù)進行執(zhí)行 function tryCallOne(fn, a) { try{ return fn(a); }catch(e){ LAST_ERROR = e; return IS_ERROR; } } //將a, b作為fn的參數(shù)執(zhí)行 function tryCallTwo(fn, a, b) { try{ return fn(a, b); }catch(e){ LAST_ERROR = e; return IS_ERROR; } }
以下是promise的構(gòu)造函數(shù)
function Promise(fn) {
/* * _deferredState:用于指示promise是否添加過deferred * _deferred:延遲對象原型是Handler * _state:promise執(zhí)行狀態(tài)pengind:0,fulfilled:1 * _value:defered函數(shù)執(zhí)行參數(shù)*/ this._deferredState = 0; this._deferred = null; this._state = 0; this._value = null; doResolved(this, fn); }
其中_deferred對象包含一個promise對象和then中定義的執(zhí)行成功(resolved)和執(zhí)行失敗(rejected)時的延遲執(zhí)行函數(shù),相當于是promise的一個鏈條,其構(gòu)造函數(shù)是如下所示的Handler
//我是deferred對象的構(gòu)造函數(shù)
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === "function"? onFulfilled: null; this.onRejected = typeof onRejected === "function"? onRejected: null; this.promise = promise; }
其他幾個屬性已經(jīng)在注釋中說明,實例化的第一步就是要執(zhí)行doResolved函數(shù)了,promise在實例化時會傳入一個包含異步操作的函數(shù)暫時稱之為fn,在這個函數(shù)中包含兩個參數(shù),這兩個參數(shù)就是執(zhí)行成功或失敗時用來調(diào)用then中函數(shù)的函數(shù)(有點繞口哈哈);而doResolved的作用就是為這個fn傳入這兩個參數(shù)而生的。
function doResolved(self, fn) {
tryCallTwo(fn, function(val){ resolve(self, val); },function(val){ reject(self, val); }); }
以上就是promise實例化的全過程了
二、promise.prototype.then的原理then的作用實際是為了創(chuàng)建一個promise鏈,先將then中聲明的函數(shù)包在promise1中,再返回一個沒有執(zhí)行邏輯的promise2,然后將后一個then中聲明的函數(shù)包在promise2中,如此構(gòu)造一個promise鏈
Promise.prototype.then = function(onFulfilled, onRejected){
var res = new Promise(noop);//一個沒有執(zhí)行邏輯的空promise handle(this, new Handler(onFulfilled, onRejected, res));//包一個promise在當前promise中 return res; };
Handler在之前已經(jīng)提到過,接下來看看handle這個函數(shù)都干了啥
//將原始的promise對象構(gòu)造為一個包含子promise的鏈式promise
function handle(self, deferred) { if(self._deferredState === 0){ self._deferredState = 1; self._deferred = deferred; return; } }
handle函數(shù)傳入兩個參數(shù),一個是promise本身,另一個是包含一個沒有處理邏輯的空promise的deferred對象,整個的處理邏輯就是將deferred對象賦給promise的deferred屬性,然后再將_deferredState切換為1,指示promise已添加過deferred對象。說白了,handle就是給聲明的promise添加一個包含空promise的deferred對象,再由then函數(shù)返回這個空promise。如此構(gòu)成一個層層包裹的promise鏈。
三、resolve&rejectfunction reject(self, newValue){
self._state = 2; self._value = newValue; if(self._deferredState === 1){handleResolved(self, self._deferred);}
}
function resolve(self, newValue){
self._state = 1; self._value = newValue; if(self._deferredState === 1){handleResolved(self, self._deferred);}
}
resolve和reject兩個函數(shù)記錄下外部傳來的參數(shù),并將promise的狀態(tài)記錄在_state屬性中,供接下來的handleResolved函數(shù)觸發(fā)promise鏈用。
function handleResolved(self, deferred) {
asap(function(){ var cb = self.state === 1 ? deferred.onFulfilled : deferred.onRejected; var ret = tryCallOne(cb, self._value); resolve(deferred.promise, ret); return; })
}
首先handleResolve本質(zhì)是一個遞歸函數(shù),結(jié)束條件是self._deferredState !== 1,即終止到未掛載deferred對象的子promise為止。另外執(zhí)行deferred對象內(nèi)部函數(shù)的代碼包裹在asap模塊中,asap模塊相當于一個插隊作用,包含在其中的任務(wù)會在當前線程的所有排隊任務(wù)隊列全部執(zhí)行完后執(zhí)行,但又會在新線程之前執(zhí)行即setTimeout中的任務(wù)之前。之前在知乎上看到關(guān)于該問題的討論,真相就是這個asap了??梢钥吹竭@個handleResolved函數(shù)的作用就是根據(jù)_state屬性逐個觸發(fā)promise鏈中的onFulFilled或onRejected函數(shù)。
以上是在對promise源碼 閱讀后,針對promise的幾個功能對整體簡化之后的代碼及總結(jié),之后會根據(jù)promise的幾個特性完善各個函數(shù),還你一個完整的promise。源碼請戳這里。
六、來一道promise測試題function timeout() { var promiseObj = new Promise(function(resolve, reject){ console.log("1"); resolve(); }); return promiseObj; } timeout().then(function(){ setTimeout(function(){console.log("2")}, 500); }).then(function(){ console.log("3") }); console.log("4");
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/94532.html
摘要:前言整理中一些相似的關(guān)鍵字方法概念。于是我們修改上面的函數(shù)來驗證它們的區(qū)別小明擼代碼擼代碼小紅小黑小剛小紅小黑擼代碼小李小謝小剛小李小謝擼代碼那么與有什么區(qū)別呢與和不同的是,綁定后不會立即執(zhí)行。通常用來處理一些并發(fā)的異步操作。 前言 整理 javascript 中一些相似的關(guān)鍵字、方法、概念。 1. var、function、let、const 命令的區(qū)別 使用var聲明的變量,其作...
摘要:本篇文章將會嘗試用簡單易懂的語言描述的原理,并且用手擼一個簡單的。一個后可以通過方法,指定和時的回調(diào)函數(shù)。實現(xiàn)實現(xiàn)狀態(tài)機因為是一個構(gòu)造函數(shù),使用的寫法,首先想到的就是有顯式聲明的。 說到Promise,都知道它是比回調(diào)函數(shù)更優(yōu)的一種異步編程解決方案,它可以使得異步操作邏輯變得更加清晰,是解決地獄回調(diào)的一種嘗試。本篇文章將會嘗試用簡單易懂的語言描述Promise的原理,并且用es6手擼一...
摘要:我們將登錄按鈕上綁上事件,點擊登錄之后向服務(wù)端提交賬號和密碼進行驗證。所以前端和后端權(quán)限的劃分是不太一致。側(cè)邊欄最后一個涉及到權(quán)限的地方就是側(cè)邊欄,不過在前 完整項目地址:vue-element-admin 系列文章: 手摸手,帶你用vue擼后臺 系列一(基礎(chǔ)篇) 手摸手,帶你用vue擼后臺 系列二(登錄權(quán)限篇) 手摸手,帶你用vue擼后臺 系列三 (實戰(zhàn)篇) 手摸手,帶你用vu...
摘要:年前無心工作,上班刷知乎發(fā)現(xiàn)一篇分享爬蟲的文章。另外攜帶的數(shù)據(jù)是用來告訴服務(wù)器當前請求是從哪個頁面請求過來的。 年前無心工作,上班刷知乎發(fā)現(xiàn)一篇分享python爬蟲的文章。 感覺他爬取的網(wǎng)站里的妹子都好好看哦,超喜歡這里的,里面?zhèn)€個都是美女。 無小意丶:自我發(fā)掘爬蟲實戰(zhàn)1:宅男女神網(wǎng)妹子圖片批量抓取,分類保存到本地和MongoDB數(shù)據(jù)庫 無奈python雖然入門過但太久沒用早已荒廢,最...
閱讀 2037·2021-11-22 14:45
閱讀 2691·2021-10-12 10:11
閱讀 824·2021-09-22 10:02
閱讀 1351·2019-08-30 15:55
閱讀 1205·2019-08-30 15:54
閱讀 3322·2019-08-30 15:54
閱讀 1305·2019-08-29 17:16
閱讀 3147·2019-08-28 17:55