都8102年爲何還要寫promise實現? 年前和年後面試了幾家公司, 雖然都掛了… 可是都談到了一個面試題,就是promise. 雖然使用promise很簡單,並且我回答的都沒問題.javascript
可是面試官都問到了一個題目. "若是讓你實現一個promise.all方法, 怎麼實現 ? " 臨時想了一個每一個promise.then裏用計數器+1, 在判斷計數器是否等於參數Array[promise]的 length 來判斷promise是否都完成的實現思路, 也不知道算不算是對的.java
而後就回來想本身能不能在不看任何人的代碼的狀況下, 實現一個promise。git
var a = new Promise(function( resolve, reject ){})github
new一個Promise實例,傳入一個函數,裏面有兩個參數。面試
resolve
:成功時調用,並將成功後的數據傳進then
方法裏。數組
reject
:失敗的時候調用,並將失敗的數據傳進catch
方法裏。promise
很簡單,只有咱們常見的then
catch
還有finally
方法。不過finally
方法應該不屬於ES6標準的,因此先忽略。(上網查了一下好像是ES2018標準)緩存
什麼都不想,先寫一個構造函數,就叫 Future 把。函數
由於Promise有兩種狀態,因此我給他加一個 status
。oop
function Future(func){ this.status = null; }
接着須要執行傳入的函數,並傳給他一個resolve
和reject
方法。常常用Promise的同窗應該知道 Promise.resolve
和 Promise.reject
。
可是沒在原型裏找到這兩個方法,因此我就直接在Future
上加這兩個方法。
// 只要執行了resolve或者reject確定要改變status, 因此對實例的status作更新 Future.resolve = function (data) { this.status = 'resolve' this.data = data } Future.reject = function (data) { this.status = 'reject' this.data = data }
這兩個這裏的data要在then裏用,因此仍是得緩存起來,而後將這兩個方法傳進func
裏,這裏對構造函數再作改動
function Future(func){ this.status = null; this.data = null; func(Future.resolve, Future.resolve) }
可是這裏有一個問題,resolve執行的時候,this並非指向當前的promise實例的,這時我就想到了bind
方法。因此必須在初始化的時候把resolve和reject的做用域給綁定好。對構造函數再次作修改。( 還要加上setTimeout , 加入even loop,這個是後加的)
function Future(func){ if(typeOf func !== "function") throw new Errow("Future 的參數必須爲函數"); var _this = this; this.status = null; this.data = null; setTimeout(function () { func(Future.resolve.bind(_this), Future.resolve.bind(_this)) }) }
回顧一下then的使用方式:傳入一個函數,在promise執行resolve後,纔會調用,而且函數的參數就是調用resolve的時候傳入的值。而且能夠return一個值。給下一個then繼續調用。
因此then函數應該很簡單,直接緩存這個函數,resolve的時候再拿出來調用便可。而關於鏈式調用,一開始想到的就是return this
因此一開始我先是這麼寫的
function Future(func){ //再加一個函數隊列數組和一個錯誤狀態的執行函數 this.queue = []; this.failCb = null; ...其他代碼省略 }
Future.prototype.then = function (callback) { if(typeof callback !== "function") throw new Errow("then必須傳入函數"); if(this.status === 'resolve'){ this.data = callback(this.data); }else if(this.status === null){ this.queue.push(callback); } return this; } Future.prototype.catch = function (callback) { if(typeof callback !== "function") throw new Errow("catch必須傳入函數"); if (this.status === 'reject') { this.data = callback(this.data); }else if(this.status === null){ this.failCb = callback; } return this; }
其餘的都好了,接着就是在resolve裏去執行隊列裏的函數。reject裏執行錯誤函數。
Future.resolve = function (data) { var context = this; context.status = 'resolve'; context.data = data; //先把第一個函數拿出來 var func = context.queue.shift(); if(func){ try{ var d = func(data); //函數能夠返回一個值,也能夠返回一個promise if(d instanceof Future){ d = d.data; } //遞歸的方式再執行下一個,這裏再用call去改變this的指向 Future.resolve.call(context, d); }catch(err){ //捕捉報錯,執行catch Future.reject.call(context, err); } } } Future.reject = function (data) { this.status = 'reject'; this.data = data; if(this.failCb){ this.failCb(data) }else{ throw new Error("promise catch") } }
以上。
到這裏呢,就是那時臨時想臨時作的初版。
固然,後面又大改了一些東西。最主要的是then函數不該該返回this。應該是一個新的promise。若是按照如今這麼作,通過多個then以後,初始的data就變成了最後一個值了。咱們但願的是要保留最初初始化的時候的那個值。
//好比 var a = new Future(function(resolve, reject){ setTimeout(function(){ console.log('success') resolve(true) }, 1000) }) a.then(function(res){ console.log(res); return "啦拉拉" }) setTimeout(function(){ a.then(function(res){ //這裏就會輸出 "啦拉拉"。其實指望的是輸出 true console.log("settimeout: ", res) }) },2000)
後來爲了解決這個,忽然陷入了牛角尖。。。花了一天才作完。水平有限,只能作到這樣了,最後附上完整代碼吧。
後來去看了看別人實現的方法。大致思路應該也是差很少的。其實就作個記錄總結,方便之後面試用。嘻嘻(^__^)。