摘要:以上實現了最簡單的一個測試代碼當然,這不能算是一個,目前僅僅實現了根據狀態調用不同的回調函數。靜態函數接下來是的各種靜態函數每一個都執行完畢后返回總結現在,一個完整的對象就完成了。
前言
說到 ES6,Promise 是繞不過的問題;如果說 ES6 的 Class 是基于 Javascript 原型繼承的封裝,那么 Promise 則是對 callback 回調機制的改進。這篇文章,不談 Promise 的實際應用;聊一下 Promise 的實現原理,從最簡單的解決方案入手,一步一步的自己實現一個 SimplePromise。
正文 入門從最簡單的 Promise 初始化和使用入手:
const pro = new Promise ((res, rej) => {}) pro.then(data => {}, err => {})
Promise 的構造函數如上,需要傳遞一個函數作為參數,這個函數有兩個變量: resolve, reject。而 Promise 有不同的執行狀態,分三種情況:Resolve, Reject, Pending。根據以上的信息,寫出最基本的 SimplePromise 的類結構:
class SimplePromise{ constructor(handler){ this._status = "PENDING" handler(this._resolve.bind(this), this._reject.bind(this))//參數函數的作用域指向Class } _resolve(){} _reject(){} }
接下來思考一下_resolve 與_reject兩個函數的作用。我們知道,Promise 根據 then 方法來執行回調,而 then 是根據狀態判斷要執行的回調函數。不難推導出,_resolve 與_reject正是根據handler的執行來進行狀態變更的,而狀態只能由Pending向Reslove或Rejected轉換,所以有:
class SimplePromise{ constructor(handler){ ... } _resolve(val){//異步返回的數據 if(this._status === "PENDING"){//保證狀態的不可逆性 this._status = "RESOLVED" this._value = val } } _reject(val){ if(this._status === "PENDING"){ this._status = "REJECTED" this._value = val } } }then的調用邏輯
下面分析 then 函數的邏輯,從調用入手:
pro.then(data => {}, err => {})
then 接收兩個參數,第一個是執行成功調用的函數,第二個是執行失敗調用的函數。
class SimplePromise{ constructor(handler){ ... } _resolve(val){ ... } _reject(val){ ... } then(success, fail){ switch (this._status){ case "PENDING": break; case "RESOLVED": success(this._value) break; case "REJECTED": fail(this._value) break; } } }
以上實現了最簡單的一個 Promise
測試代碼:
const pro = new SimplePromise(function(res, rej) { let random = Math.random() * 10 if(random > 5){ res("success") } else{ rej("fail") } }) pro.then(function(data) { console.log(data) }, function(err) { console.log(err) })
當然,這不能算是一個 Promise,目前僅僅實現了根據狀態調用不同的回調函數。還沒有實現異步。
那如何實現異步呢?關鍵在于 then 函數,當判斷_status為PENDING時,如何延后調用 success與fail函數,等待狀態改變后再調用?
這里采用數組來存儲 fail 與 success 函數:
class SimplePromise{ constructor(handler){ this.status = "PENDING" this._onSuccess = []//存儲fail 與 success 函數 this._onFail = [] handler(this._resolve.bind(this), this._reject.bind(this)) } _resolve(val){ if(this.status === "PENDING"){ ... let temp while(this._onSuccess.length > 0){//依次執行onSuccess中的回調函數 temp = this._onSuccess.shift() temp(val) } } } _reject(val){ if(this.status === "PENDING"){ ... let temp while(this._onFail.length > 0){ temp = this._onFail.shift() temp(val) } } } then (success, fail){ switch (this.status){ case "PENDING": this._onSuccess.push(success) this._onFail.push(fail) break; ... } } }
使用 onSuccess 和 onFail 來存儲回調函數,當處理狀態為 PENDING 時,將回調函數 push 到相應的數組里,當狀態變更后,依次執行數組里的回調函數。
測試代碼:
const pro = new SimplePromise(function(res, rej) { setTimeout(function(){ let random = Math.random() * 10 if(random > 5){ res("success") } else{ rej("fail") } }, 2000) }) pro.then(function(data) { console.log(data) }, function(err) { console.log(err) })
兩秒后,會執行相應的回調。
到目前為止,最最最簡單的一個 Promise 骨架已經基本完成了。但是還有很多功能待完成。現在可以稍微休息一下,喝個咖啡打個雞血,回來我們會繼續讓這個 Promise 骨架更加豐滿起來。
. . . . . .
完善Promise歡迎回來,下面我們繼續完善我們的 Promise。
上面完成了一個最基礎的 Promise,然而還遠遠不夠。首先,Promise 需要實現鏈式調用,其次 Promise 還需要實現 all race resolve reject 等靜態函數。
首先,如何實現 then 的鏈式調用呢?需要 then 返回的也是一個 Promise。
于是有
class SimplePromise{ ... then(success, fail){ return new SimplePromise((nextSuccess, nextFail) => { const onFullfil = function(val){ const res = success(val) nextSuccess(res) } const onReject = function(val){ const res = fail(val) nextSuccess(res) ; } switch (this._status){ case "PENDING": this._onSuccess.push(onFullfil) this._onFail.push(onReject) break; case "RESOLVED": onFullfil(this._value) break; case "REJECTED": onReject(this._value) break; } }) } }
測試代碼:
const sp = new SimplePromise(function (res, rej){ setTimeout(function(){ let random = Math.random() * 10 random > 5 ? res(random) : rej(random) }, 1000) }) sp.then(data => { console.log("more than 5 " + data) return data }, err =>{ console.log("less than 5 " + err) return err }).then((data) => { console.log(data) })then的參數限制
完成了鏈式調用,then 方法還有許多其他限制:
下面思考 以下問題:代碼中四個使用 promise 的語句之間的不同點在哪兒?
假設 doSomething 也 doSomethingElse 都返回 Promise
doSomething().then(function () { return doSomethingElse(); }).then(finalHandler); doSomething().then(function () { doSomethingElse(); }).then(finalHandler);; doSomething().then(doSomethingElse()).then(finalHandler);; doSomething().then(doSomethingElse).then(finalHandler);;
答案 一會兒再揭曉,我們先來梳理以下then 方法對傳入不同類型參數的處理機制:
直接上代碼:
class SimplePromise{ ... then(success, fail){ return new SimplePromise((nextSuccess, nextFail) => { const onFullfil = function(val){ if(typeof success !== "function"){ nextSuccess(val) } else{ const res = success(val)//success 的返回值 if(res instanceof SimplePromise){//如果success 返回一個promise 對象 res.then(nextSuccess, nextFail) } else{ nextSuccess(res) } } } if(fail){ const onReject = function(val){ if(typeof fail !== "function"){ nextSuccess(val) } else{ const res = fail(val) if(res instanceof SimplePromise){ res.then(nextSuccess, nextFail) } else{ nextSuccess(res) } } } } else{ onReject = function(){} } switch (this._status){ case "PENDING": this._onSuccess.push(onFullfil) this._onFail.push(onReject) break; case "RESOLVED": onFullfil(this._value) break; case "REJECTED": onReject(this._value) break; } }) } }
對于傳入 then 方法的參數,首先判斷其是否為 function,判斷為否,直接執行 下一個 then 的 success 函數;判斷為是,接著判斷函數的返回值 res 類型是否為 Promise,如果為否,直接執行下一個 then 的 success 函數,如果為是,通過 then 調用接下來的函數。
所以,上面的問題就不難得到答案了。
doSomething().then(function () { return doSomethingElse();//返回值為Promise }).then(finalHandler); RETURN: doSomething --->doSomethingElse(undefined) ---> final(doSomethingElseResult)
doSomething().then(function () { doSomethingElse();//返回值為 undefined }).then(finalHandler); RETURN: doSomething --->doSomethingElse(undefined) ---> final(undefined)
doSomething().then(doSomethingElse())//參數 typeof != function .then(finalHandler); RETURN: doSomething doSomethingElse(undefined) ---> final(doSomethingResult)
doSomething().then(doSomethingElse)//與1的調用方式是不同的 .then(finalHandler); RETURN: doSomething --->doSomethingElse(doSomethingResult) ---> final(doSomethingElseResult)
好,then 方法已經完善好了。
靜態函數接下來是 Promise 的各種靜態函數
class SimplePromise(){ ... static all(){} static race(){} static resolve(){} static reject(){} }all
static all(promiselist){ if(Array.isArray(promiselist)){ const len = promiselist.length; const count = 0 const arr = [] return new SimplePromise((res, rej) => { for(let i = 0; irace{ arr[i] = data count ++ if(count === len){//每一個Promise都執行完畢后返回 res(arr) } }, err => { rej(err) }) } }) } }
static race(promiselist){ if(Array.isArray(promiselist)){ const len = promiselist.length return new SimplePromise((res, rej) =>{ promiselist.forEach(item =>{ this.resolve(item).then(data => { res(data) }, err =>{ rej(err) }) }) }) } }resolve
static resolve(obj){ if(obj instanceof SimplePromise){ return obj } else { return new SimplePromise((res) =>{ res(obj) }) } }reject
static reject(obj){ if(obj instanceof SimplePromise){ return obj } else { return new SimplePromise((res, rej) =>{ rej(obj) }) } }總結
現在,一個完整的 Promise 對象就完成了。現在來總結一下 callback 回調和 Promise 的異同吧。
其實,不管是 callback 還是 Promise,這二者都是將需要滯后執行方法而提前聲明的方式,只不過 callback 的處理方式比較粗獷,將 cb 函數放到異步執行的結尾;而 Promise 優于 cb 的是通過定義了不同的執行狀態,更加細致的進行結果處理,提供了很好的 catch 機制,這是其一;其二,then 的鏈式調用解決了 cb 的回調地獄;但是 then 的鏈式調用也不是很好的解決方案,如果封裝不好,then里面套用大量的代碼的話也會引起代碼的不美觀和閱讀上的困難,這一方面的終極解決方法還是 es7 的 async/await。
這篇文章的代碼是幾個星期以前寫的,參考的是思否上的一篇關于promise的文章;總結的是我對promise的理解和思考,如果有不準確或錯誤的地方還希望各位不吝賜教!希望大家積極反饋,一起交流!
參考文檔談一談使用 Promise 的反模式 寫這篇文章的時候,我是參考我兩周前的代碼寫的,當時的代碼思路來源于思否上的謀篇博客,等我找到會貼上來
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100951.html
摘要:點燃引擎這是一個組件實現組件可交互所需的流程,輸出虛擬,虛擬轉為,再在上注冊事件,事件觸發修改數據,在每次調用方法時,會自動執行方法來更新虛擬,如果組件已經被渲染,那么還會更新到中去。 Part one - setState點燃引擎 showImg(https://segmentfault.com/img/bVbdGkJ?w=849&h=270); 這是一個React組件實現組件可交互...
摘要:組合繼承實現了屬性分離,方法共享下的完美繼承方案繼承我們的主角,,就是對組合繼承的改進。這也是為什么在子類構造函數中一定要顯示調用的原因。 談到繼承,或者更廣義上的:一個對象可以使用另外一個對象的屬性或方法。實現起來無外乎有兩種方式:apply or call 改變this的作用域原型繼承 改變__proto__指向,添加作用域鏈 而JavaScript所有的繼承實現,都是圍繞以上兩點...
摘要:防止類的構造函數以普通函數的方式調用。這個函數的主要作用是通過給類添加方法,其中將靜態方法添加到構造函數上,將非靜態的方法添加到構造函數的原型對象上。 Class是ES6中新加入的繼承機制,實際是Javascript關于原型繼承機制的語法糖,本質上是對原型繼承的封裝。本文將會討論:1、ES6 class的實現細2、相關Object API盤點3、Javascript中的繼承實現方案盤點...
摘要:前言與是目前圈子內比較活躍的前端構建工具。對于初學者來說,對這二者往往容易認識不清,今天,就從事件的源頭,說清楚與。它可以將許多松散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。打包后形成的文件出口。 前言:Webpack 與 gulp是目前圈子內比較活躍的前端構建工具。網上有很多二者比較的文章,面試中也會經常遇到gulp,Webpack的區別這樣的問題。對于初學者來說,對這二...
閱讀 2860·2019-08-30 15:44
閱讀 1888·2019-08-29 13:59
閱讀 2845·2019-08-29 12:29
閱讀 1090·2019-08-26 13:57
閱讀 3202·2019-08-26 13:45
閱讀 3330·2019-08-26 10:28
閱讀 825·2019-08-26 10:18
閱讀 1695·2019-08-23 16:52