摘要:大家都知道怎么用,但是對于內部的原理很多人都不是很清楚來看一個面試題的是怎么實現的首先來分析一下是屬于實例上的方法參數有個,分別為,并且都是可選的可以實現鏈式調用執行要執行或者方法參數,分別有自己的參數,分別是的參數跟的參數只能使用前一個的
Promise 大家都知道怎么用, 但是對于內部的原理很多人都不是很清楚
來看一個面試題: Promise的then 是怎么實現的
首先來分析一下then
then是屬于實例上的方法
參數有2個,分別為onFulfilled, onRejected,并且都是可選的
可以實現鏈式調用
then執行要執行Promise onFulfilled 或者 onRejected 方法
參數onFulfilled,onRejected 分別有自己的參數, 分別是resolve的參數跟reject的參數
then只能使用前一個then的返回值
then返回值不能是同一個promise
來一個一個看吧
then是屬于實例上的方法
Promise.prototype.then = function(){}
參數有2個,分別為onFulfilled, onRejected,并且都是可選的
Promise.prototype.then = function(onFulfilled,onRejected){}
可以實現鏈式調用
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise(function(resolve,reject){ // 代碼省略 }) }
要實現promise鏈式 返回的必須是一個Promise 再由于status 改變狀態了不能再變 所以需要第二個.... 第N個promise 調用新的resolve
then執行要執行Promise onFulfilled 或者 onRejected 方法
Promise.prototype.then = function(onFulfilled,onRejected){ var self = this; // 保存當前上下文 return new Promise(function(resolve,reject){ if(self.status === "resolved"){ onFulfilled(self.res) } if(self.status === "rejected"){ onRejected(self.err) } }) }
Promise.resolve(res) 、、 同步代碼的執行情況下 上述邏輯成立
參數onFulfilled,onRejected 分別有自己的參數, 分別是resolve的參數跟reject的參數
res 跟err 來源如下
function Promise(executor){ let self = this this.status = "pending" // Promise 的狀態值 this.res = undefined // 存成功之后的值 this.err = undefined // 存失敗之后的值 executor(resolve,reject) // 省略代碼 }
executor 有2個參數 分別為resolve,reject
當調用
new Promise((resolve,reject)=>{ setTimeout(()=>{ if(true){ resolve("res") }else{ reject("err") } },1000) })
executor 會執行 并且把成功的值或者失敗的值拋出來,resolve跟reject也是2個函數 定義在Promise內部
function Promise(executor){ // ...代碼省略 function resolve(res){ self.res = res } function reject(err){ self.err = err } executor(resolve,reject) }
resolve 跟reject 還需要改變 Promise的狀態值 并且一旦發生改變之后不能再次改變
function Promise(executor){ // 代碼省略 function resolve(res){ if(self.status === "pending"){ self.status = "resolved" self.res = res } } function reject(err){ if(self.status === "pending"){ self.status = "rejected" self.err = err } } executor(resolve,reject) }
我們在executor中操作的往往是異步代碼, 這個之后直接then, status的狀態值并未發生改變, 所以不會執行onFulfilled跟onRejected,
這個時候我們需要訂閱
function Promise(executor){ // 代碼省略 this.onFulfilledCallback = [] // 存成功之后的回調 this.onrejectedCallback = [] // 存失敗之后的回調 function resolve(res){ if(self.status === "pending"){ self.status = "resolved" self.res = res } } function reject(err){ if(self.status === "pending"){ self.status = "rejected" self.err = err } } executor(resolve,reject) } new Promise(executor).then((onFulfilled,onrejected)=>{ var self = this; // 保存當前上下文 **注意: 第二次調用then this是指向new Promise的** return new Promise(function(resolve,reject){ // ...代碼省略 if(self.status === "pending"){ self.onFulfilledCallback.push(function(){ // 把成功之后需要做的事存起來 有多少個then就有多少個函數 onFulfilled(self.res) // 注意 這里的self.res 會隨著then的調用發生改變 因為每次then都new 了一個Promise }) self.onrejectedCallback.push(function(){ // 把失敗之后的事存起來 有多少個then就有多少個函數 onFulfilled(self.err) }) } }) })
當resolve的時候 或者reject的時候
一一拿出來執行
function resolve(res){ // 省略代碼 self.res = res onFulfilledCallback.forEach(fn=>{ fn() }) }
實現鏈式調用我們需要對then函數執行的返回值做一個操作!!!
需求:
then的參數onFulfilled函數 需要接收到上一個onfulfilled的執行結果
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise((resolve,reject)=>{ // ...代碼省略 if(self.status === "resolved"){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這里執行Promise.resolve() 同步代碼 // 然后把x傳遞給下一個then resolve(x) } }) }
如果resolve是異步的話
Promise.prototype.then = function(onFulfilled,onRejected){ return new Promise((resolve,reject)=>{ // ...代碼省略 if(self.status === "resolved"){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這里執行的是Promise.resolve() 同步代碼 // 然后把x傳遞給下一個then resolve(x) } if(self.status === "pending"){ self.onFulfilledCallback.push(function(){ let x = onFulfilled(self.res) // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當于 fn(){let x = onFulfilled} resolve(x) }) } }) } // 同時為了拿到 fn(){let x = onFulfilled ...} 得值 傳遞給下一個onFulfilled,onFulfilledCallback需要改寫 onRejectedCallback同理 onFulfilledCallback.forEach(fn=>{ return fn() }) onRejectedCallback.forEach(fn=>{ return fn() })
-
onFullfillled 的返回值可能是個promise 也可能是個普通值
考慮如下情況:
1. 返回值是個promise
Promsie.resolve(11).then(res=>{ return new Promise(executor) })
2. 返回值是個普通值
Promsie.resolve(11).then(res=>{ return 1 })
最關鍵的來了
我們需要對onFullfillled 的返回值進行判斷
修改then函數
// ...代碼省略 if(self.status === "resolved"){ let x = onFulfilled(self.res) // 拿到onFulfilled的執行結果 注意:這里執行的是Promise.resolve() 同步代碼 // 然后把x傳遞給下一個then promiseResolve(x,resolve,reject) }
function promiseResolve(x,resolve,reject){ if((typeof x != null && typeof x == "object") || typeof x == "function"){ // 確定是個引用類型 if (x instanceof Promise){ // 確定是個Promise 實例 let then = x.then then.call(x,res=>{ // 保證x調用res=>{}, res=>{}是then的onFulfilled方法 一直判斷直到onFulfilled 返回的是個普通值 然后resolve出去 promiseResolve(res,resolve,reject) },err=>{ reject(err) }) } }else{ resolve(x) } }
then返回值不能是同一個promise
考慮這樣極端問題:
let p = new Promise(resolve=>{ resolve(3333) }) let p2 = p.then(resolve=>{ return p2 })
這樣會出現什么問題呢, onFulfilled的返回結果如果是一個promise 上面的遞歸調用已經說明了 我們要不斷的去遞歸最終的onFulfilled結果 然后再改變p2的status 這樣變成了p2去等待p2的執行結果 函數死掉了
所以onFulfilled的返回結果需要跟then的返回結果去比較 修改函數
function promiseResolve(x,resolve,reject,promise){ if(x === promise ){ return new Error("引用錯誤!") } if((typeof x != null && typeof x == "object") || typeof x == "function"){ // 確定是個引用類型 if (x instanceof Promise){ // 確定是個Promise 實例 let then = x.then then.call(x,res=>{ // 保證x調用res=>{}, res=>{}是then的onFulfilled方法 一直判斷直到onFulfilled 返回的是個普通值 然后resolve出去 promiseResolve(res,resolve,reject) },err=>{ reject(err) }) } }else{ resolve(x) } }
Promise.prototype.then = function(onFulfilled,onRejected){ // ...代碼省略 let promise2 = new Promise((resolve,reject)=>{ if(self.status === "pending"){ self.onFulfilledCallback.push(function(){ let x = onFulfilled(self.res) // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當于 fn(){let x = onFulfilled} promiseResolve(x,resolve,reject,promise2) }) } }) return promise2 }
這段代碼還是有錯誤, 由于javascript主線程是單線程的, 所以在then里的promiseResolve是拿不到promise2的 所以我們需要開啟異步 使用定時器或者nextTick 這里我們用定時器
Promise.prototype.then = function(onFulfilled,onRejected){ // ...代碼省略 let promise2 = new Promise((resolve,reject)=>{ if(self.status === "pending"){ self.onFulfilledCallback.push(function(){ setTimeout(()=>{ let x = onFulfilled(self.res) // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當于 fn(){let x = onFulfilled} promiseResolve(x,resolve,reject,promise2) },0) }) } }) return promise2 }
此時 整個then差不多完成了
需要補充的點onFulfilled,onRejected 有可能是undefined 這里未做判斷
在new Promise 時 executor函數內部有可能報錯 這里未使用try catch捕獲
這里對于onRejected的錯誤未拋出
總結then每次創建一個新的promise對象 對于同步的resolve,reject直接調用onFulfilled或者onRejected ,對于異步的resolve,reject使用
訂閱發布模式,把每個resolve,reject 暫存起來 等達到條件時候一一執行, 并且拿到返回結果去跟內部的promise比較,并且判斷如果是一個promise的話,不斷解析onFulfilled 的返回結果 直至resolve出去
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102006.html
摘要:通過構造函數,可以創建一個。函數本身是一個異步行為,其方法的第三個參數為一個回調函數,用來接收文件讀取的結果失敗時候的和成功時候的。第一個函數的參數對應的參數,第二個回調函數對應的參數。 一:Promise的概念 Promise的中文意思是‘承諾’,什么叫承諾?承諾就是現在沒有發生,在將來的某個時刻一定會發生的事情。放在編程語言的環境下,Promise就是異步事件的結果的占位符。我們不...
摘要:作者珂珂滬江前端開發工程師本文為原創文章,有不當之處歡迎指出。只對未來發生的事情做出兩種基本情況的應對成功和失敗。在異步轉同步這條道路上,只是一個出彩的點,他還尚有一些缺陷和不足,并不是我們最終的解決方案。 作者:珂珂 (滬江前端開發工程師)本文為原創文章,有不當之處歡迎指出。轉載請標明出處。 一個新事物的產生必然是有其歷史原因的。為了更好的以同步的方式寫異步的代碼,人們在JS上操碎了...
摘要:本文從入手,系統的回顧的異步機制及發展歷程。需要提醒的是,文本沒有討論的異步機制。這就是之前提到的事件觸發線程。其實無論是請求還是定時器還是事件,我們都可以統稱它們為事件。第二階段,引擎線程專注于處理事件。將外元素的事件回調放入調用棧。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....
摘要:如何寫一個符合規范的實現前言是異步編程的一種解決方案從語法上講,是一個對象,從它可以獲取異步操作的消息從本意上講,它是承諾,承諾它過一段時間會給你一個結果。 如何寫一個符合promiseA+規范的promise實現 前言 Promise 是異步編程的一種解決方案:從語法上講,promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。pr...
摘要:處理和前一個回調函數運行時發生的錯誤發生錯誤對象的錯誤具有冒泡性質,會一直向后傳遞,直到被捕獲為止。 0 前言 我一直以為我對Promise比較了解,相關的方法已經非常熟悉了,直到我看到這篇文章,里面提出了這樣一個問題:Q: 假定 doSomething() 和 doSomethingElse() 均返回 promises,下面的四種 promises 的區別是什么 /...
閱讀 662·2021-11-24 09:39
閱讀 2315·2021-11-22 13:54
閱讀 2197·2021-09-23 11:46
閱讀 3246·2019-08-30 15:55
閱讀 2679·2019-08-30 15:54
閱讀 2403·2019-08-30 14:18
閱讀 1546·2019-08-29 14:15
閱讀 2732·2019-08-29 13:49