摘要:先直接上源碼吧。阮一峰在基礎篇提到過,阮一峰基礎介紹,返回的是一個新的實例,不是原來的那個實例。同理綁定和的指向。秒后,是為了讓在隊列的最后執行。此時將中第一個回調函數執行的賦值給了。這就驗證了阮一峰在基礎介紹將的下面的代碼邏輯。
先直接上源碼吧。
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"; } //為了讓傳進來的函數在then后執行 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; }
之前寫過如何構造一個promise庫,參考的美團點評的網站,寫到后面發現Promise.resolve()直接調用就會出現問題。報錯上面的庫,也是引用Talking Coder的一篇博客,逐漸開始理解promise內部是如何實現的了,當然還是有一些疑問。比如上面的庫,需要注釋window.Promise = Promise和if(!window.Promise) {} 內部的代碼在debug的時候才能看得見。但如果注釋掉,直接Promise.resolve()又回同樣報resolve不是一個方法。
阮一峰在promise基礎篇提到過,阮一峰promise基礎介紹,then返回的是一個新的Promise實例,不是原來的那個實例。這里提到的原來的那個實例,我想應該是第一次實例化的Promise實例對象。這里有點不懂,為何then函數在return this后,就會返回一個新的Promise實例對象。
大概講解下此Promise庫的原理吧。
setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0);
call 和 bind 都是為了綁定this的指向,因為直接回調,this在瀏覽器里面指向的是window對象,綁定fn執行的時候this指向Promise的實例對象。同理綁定resolve和reject的this指向。
setTimout 0秒后,是為了讓fn在隊列的最后執行。這里的隊列一會再剖析。或者說讓resolve或reject在隊列的最后執行。
如果去掉setTimeout 0秒后,那么在實例化Promise的時候,就會立刻執行回調fn,進而執行resolve或reject函數,而此時then還未來得及push需要thenAble的回調隊列,導致再執行resolve或reject里面的triggerThen()方法時,無法執行(此時回調隊列為空。)
Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); return this; }
then 實際是一個回調隊列,當有3個then,那么回調隊列就會有三個。然后通this.triggerThen()
以此遞歸調用,直接回調隊列被調用完畢后,再執行fn中的resolve或reject函數。
再分析下狀態和返回值
在triggerThen函數里有這么幾行代碼
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(); }
可以看出,當js無語法報錯的時候,執行的try代碼。此時將then()中第一個回調函數執行的value賦值給了this.value。在new Promise()的回調函數中,如果執行過resolve()的話,那么此時賦值就是第二次賦值了。同理this.status。當出現語法報錯的時候,會執行catch,此時將錯誤參數e同樣賦值給this.value。狀態也被變為rejected。同理如果在new Promise()回調中執行過reject,那么此時賦值也是第二次賦值了。這就驗證了阮一峰在Promise基礎介紹將的下面的代碼邏輯。
const promise = new Promise(function(resolve, reject) { throw new Error("test"); }); promise.catch(function(error) { console.log(error); }); // Error: test
catch 跟then的第二個參數是一個邏輯。而且then的第二個參數實際我們很少回調。都寫catch來異常捕獲的。
其實寫這篇文章的時候,對Promise還是了解在表面上,但收獲依然還是有的,準建理解了Promise是如何實現的,比如resolve和then回調的關系,
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93105.html
摘要:從源碼看概念與實現是異步編程中的重要概念,它較好地解決了異步任務中回調嵌套的問題。這些概念中有趣的地方在于,標識狀態的變量如都是形容詞,用于傳入數據的接口如與都是動詞,而用于傳入回調函數的接口如及則在語義上用于修飾動詞的副詞。 從源碼看 Promise 概念與實現 Promise 是 JS 異步編程中的重要概念,它較好地解決了異步任務中回調嵌套的問題。在沒有引入新的語言機制的前提下,這...
摘要:因此,當作為參數的執行任意結果的回調函數時,就會將參數傳遞給外層的,執行對應的回調函數。 背景 在上一篇博客[[譯]前端基礎知識儲備——Promise/A+規范](https://segmentfault.com/a/11...,我們介紹了Promise/A+規范的具體條目。在本文中,我們來選擇了promiz,讓大家來看下一個具體的Promise庫的內部代碼是如何運作的。 promiz...
摘要:最近讀了的源碼,理清楚了架構設計與用到的第三方庫。本系列將分為篇,分別介紹的架構設計和個核心庫,最終會手動實現一個簡易的。本文來自心譚博客深入源碼核心庫原理所有系列文章都放在了。這一段邏輯封裝在了核心庫里面。 最近讀了 koa2 的源碼,理清楚了架構設計與用到的第三方庫。本系列將分為 3 篇,分別介紹 koa 的架構設計和 3 個核心庫,最終會手動實現一個簡易的 koa。這是系列第 2...
開頭 首先本文有將近3000字,閱讀可能會占用你20分鐘左右。 文筆可能不佳,希望能幫助到閱讀此文的人有一些收獲 在進行源碼閱讀前首先抱有一個疑問,thunk函數是什么,thunkify庫又是干什么的,co又是干嘛,它有啥用 程序語言有兩種求值策略 傳名調用 傳入參數實際上是傳入函數體 傳值調用 函數體在進入的時候就進行運算計算值 編譯器的傳名調用實現,往往是將參數放到一個臨時函數之中,再將這個...
閱讀 2106·2021-11-24 09:39
閱讀 1495·2019-08-30 15:44
閱讀 1946·2019-08-29 17:06
閱讀 3393·2019-08-29 16:32
閱讀 3543·2019-08-29 16:26
閱讀 2654·2019-08-29 15:35
閱讀 3026·2019-08-29 12:50
閱讀 1636·2019-08-29 11:15