摘要:先直接上源碼吧。阮一峰在基礎(chǔ)篇提到過,阮一峰基礎(chǔ)介紹,返回的是一個(gè)新的實(shí)例,不是原來(lái)的那個(gè)實(shí)例。同理綁定和的指向。秒后,是為了讓在隊(duì)列的最后執(zhí)行。此時(shí)將中第一個(gè)回調(diào)函數(shù)執(zhí)行的賦值給了。這就驗(yàn)證了阮一峰在基礎(chǔ)介紹將的下面的代碼邏輯。
先直接上源碼吧。
if(!window.Promise) { function Promise(fn) { var self=this; this.status = "pending"; this.thenCache = []; this.count = 0 if(!(this instanceof Promise)) { throw "Defer is a constructor and should be called width "new" keyword"; } if(typeof fn !== "function") { throw "Defer params must be a function"; } //為了讓傳進(jìn)來(lái)的函數(shù)在then后執(zhí)行 setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0); } Promise.prototype.resolve = function(value) { this.value = value; this.status = "resolved"; this.triggerThen(); } Promise.prototype.reject = function(reason) { this.value = reason; this.status = "rejected"; this.triggerThen(); } Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); console.log("this", this) return this; } Promise.prototype.catch = function(fn) { if(typeof fn === "function") { this.errorHandle = fn; } }; Promise.prototype.triggerThen = function() { console.log("this.thenCache", this.thenCache) console.log("this.status", this.status) var current = this.thenCache.shift(), res; console.log("current", current) if(!current && this.status === "resolved") { // console.log("--11--", + new Date()) return this; } else if(!current && this.status === "rejected") { if(this.errorHandle) { this.value = this.errorHandle.call(undefined, this.value); this.status = "resolved"; console.log("--11--", + new Date()) } return this; }; if(this.status === "resolved") { // console.log("--222--", + new Date()) res = current.onResolve; } else if(this.status === "rejected") { console.log("--222--", + new Date()) res = current.onReject; } this.count ++; if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = "resolved"; console.log("-ffffd--", + new Date()) this.triggerThen(); // console.log("this.count", this.count, + new Date()) } catch(e) { this.status = "rejected"; this.value = e; return this.triggerThen(); } } else { console.log("--44--") this.triggerThen(); } } window.Promise = Promise; }
之前寫過如何構(gòu)造一個(gè)promise庫(kù),參考的美團(tuán)點(diǎn)評(píng)的網(wǎng)站,寫到后面發(fā)現(xiàn)Promise.resolve()直接調(diào)用就會(huì)出現(xiàn)問題。報(bào)錯(cuò)上面的庫(kù),也是引用Talking Coder的一篇博客,逐漸開始理解promise內(nèi)部是如何實(shí)現(xiàn)的了,當(dāng)然還是有一些疑問。比如上面的庫(kù),需要注釋window.Promise = Promise和if(!window.Promise) {} 內(nèi)部的代碼在debug的時(shí)候才能看得見。但如果注釋掉,直接Promise.resolve()又回同樣報(bào)resolve不是一個(gè)方法。
阮一峰在promise基礎(chǔ)篇提到過,阮一峰promise基礎(chǔ)介紹,then返回的是一個(gè)新的Promise實(shí)例,不是原來(lái)的那個(gè)實(shí)例。這里提到的原來(lái)的那個(gè)實(shí)例,我想應(yīng)該是第一次實(shí)例化的Promise實(shí)例對(duì)象。這里有點(diǎn)不懂,為何then函數(shù)在return this后,就會(huì)返回一個(gè)新的Promise實(shí)例對(duì)象。
大概講解下此Promise庫(kù)的原理吧。
setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0);
call 和 bind 都是為了綁定this的指向,因?yàn)橹苯踊卣{(diào),this在瀏覽器里面指向的是window對(duì)象,綁定fn執(zhí)行的時(shí)候this指向Promise的實(shí)例對(duì)象。同理綁定resolve和reject的this指向。
setTimout 0秒后,是為了讓fn在隊(duì)列的最后執(zhí)行。這里的隊(duì)列一會(huì)再剖析?;蛘哒f讓resolve或reject在隊(duì)列的最后執(zhí)行。
如果去掉setTimeout 0秒后,那么在實(shí)例化Promise的時(shí)候,就會(huì)立刻執(zhí)行回調(diào)fn,進(jìn)而執(zhí)行resolve或reject函數(shù),而此時(shí)then還未來(lái)得及push需要thenAble的回調(diào)隊(duì)列,導(dǎo)致再執(zhí)行resolve或reject里面的triggerThen()方法時(shí),無(wú)法執(zhí)行(此時(shí)回調(diào)隊(duì)列為空。)
Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); return this; }
then 實(shí)際是一個(gè)回調(diào)隊(duì)列,當(dāng)有3個(gè)then,那么回調(diào)隊(duì)列就會(huì)有三個(gè)。然后通this.triggerThen()
以此遞歸調(diào)用,直接回調(diào)隊(duì)列被調(diào)用完畢后,再執(zhí)行fn中的resolve或reject函數(shù)。
再分析下狀態(tài)和返回值
在triggerThen函數(shù)里有這么幾行代碼
if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = "resolved"; this.triggerThen(); } catch(e) { this.status = "rejected"; this.value = e; return this.triggerThen(); } } else { this.triggerThen(); }
可以看出,當(dāng)js無(wú)語(yǔ)法報(bào)錯(cuò)的時(shí)候,執(zhí)行的try代碼。此時(shí)將then()中第一個(gè)回調(diào)函數(shù)執(zhí)行的value賦值給了this.value。在new Promise()的回調(diào)函數(shù)中,如果執(zhí)行過resolve()的話,那么此時(shí)賦值就是第二次賦值了。同理this.status。當(dāng)出現(xiàn)語(yǔ)法報(bào)錯(cuò)的時(shí)候,會(huì)執(zhí)行catch,此時(shí)將錯(cuò)誤參數(shù)e同樣賦值給this.value。狀態(tài)也被變?yōu)閞ejected。同理如果在new Promise()回調(diào)中執(zhí)行過reject,那么此時(shí)賦值也是第二次賦值了。這就驗(yàn)證了阮一峰在Promise基礎(chǔ)介紹將的下面的代碼邏輯。
const promise = new Promise(function(resolve, reject) { throw new Error("test"); }); promise.catch(function(error) { console.log(error); }); // Error: test
catch 跟then的第二個(gè)參數(shù)是一個(gè)邏輯。而且then的第二個(gè)參數(shù)實(shí)際我們很少回調(diào)。都寫catch來(lái)異常捕獲的。
其實(shí)寫這篇文章的時(shí)候,對(duì)Promise還是了解在表面上,但收獲依然還是有的,準(zhǔn)建理解了Promise是如何實(shí)現(xiàn)的,比如resolve和then回調(diào)的關(guān)系,
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/93105.html
摘要:從源碼看概念與實(shí)現(xiàn)是異步編程中的重要概念,它較好地解決了異步任務(wù)中回調(diào)嵌套的問題。這些概念中有趣的地方在于,標(biāo)識(shí)狀態(tài)的變量如都是形容詞,用于傳入數(shù)據(jù)的接口如與都是動(dòng)詞,而用于傳入回調(diào)函數(shù)的接口如及則在語(yǔ)義上用于修飾動(dòng)詞的副詞。 從源碼看 Promise 概念與實(shí)現(xiàn) Promise 是 JS 異步編程中的重要概念,它較好地解決了異步任務(wù)中回調(diào)嵌套的問題。在沒有引入新的語(yǔ)言機(jī)制的前提下,這...
摘要:因此,當(dāng)作為參數(shù)的執(zhí)行任意結(jié)果的回調(diào)函數(shù)時(shí),就會(huì)將參數(shù)傳遞給外層的,執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。 背景 在上一篇博客[[譯]前端基礎(chǔ)知識(shí)儲(chǔ)備——Promise/A+規(guī)范](https://segmentfault.com/a/11...,我們介紹了Promise/A+規(guī)范的具體條目。在本文中,我們來(lái)選擇了promiz,讓大家來(lái)看下一個(gè)具體的Promise庫(kù)的內(nèi)部代碼是如何運(yùn)作的。 promiz...
摘要:最近讀了的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。本系列將分為篇,分別介紹的架構(gòu)設(shè)計(jì)和個(gè)核心庫(kù),最終會(huì)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的。本文來(lái)自心譚博客深入源碼核心庫(kù)原理所有系列文章都放在了。這一段邏輯封裝在了核心庫(kù)里面。 最近讀了 koa2 的源碼,理清楚了架構(gòu)設(shè)計(jì)與用到的第三方庫(kù)。本系列將分為 3 篇,分別介紹 koa 的架構(gòu)設(shè)計(jì)和 3 個(gè)核心庫(kù),最終會(huì)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的 koa。這是系列第 2...
開頭 首先本文有將近3000字,閱讀可能會(huì)占用你20分鐘左右。 文筆可能不佳,希望能幫助到閱讀此文的人有一些收獲 在進(jìn)行源碼閱讀前首先抱有一個(gè)疑問,thunk函數(shù)是什么,thunkify庫(kù)又是干什么的,co又是干嘛,它有啥用 程序語(yǔ)言有兩種求值策略 傳名調(diào)用 傳入?yún)?shù)實(shí)際上是傳入函數(shù)體 傳值調(diào)用 函數(shù)體在進(jìn)入的時(shí)候就進(jìn)行運(yùn)算計(jì)算值 編譯器的傳名調(diào)用實(shí)現(xiàn),往往是將參數(shù)放到一個(gè)臨時(shí)函數(shù)之中,再將這個(gè)...
摘要:參考函數(shù)庫(kù)是用于函數(shù)自動(dòng)執(zhí)行的一個(gè)小工具。是一個(gè)函數(shù)函數(shù)返回一個(gè)函數(shù)執(zhí)行完成上面代碼中,等到函數(shù)執(zhí)行結(jié)束,就會(huì)輸出一行提示。函數(shù)其實(shí)就是將兩種自動(dòng)執(zhí)行器函數(shù)和對(duì)象,包裝成一個(gè)庫(kù)。使用的前提是,函數(shù)內(nèi)的命令后面,只能是函數(shù)或者對(duì)象。 參考 reference 1 reference 2 co 函數(shù)庫(kù)是用于 Generator 函數(shù)自動(dòng)執(zhí)行的一個(gè)小工具。 usge var co = r...
閱讀 2198·2021-11-24 09:39
閱讀 1557·2019-08-30 15:44
閱讀 2004·2019-08-29 17:06
閱讀 3453·2019-08-29 16:32
閱讀 3604·2019-08-29 16:26
閱讀 2711·2019-08-29 15:35
閱讀 3071·2019-08-29 12:50
閱讀 1701·2019-08-29 11:15