摘要:手寫一個的實現。當注冊的回調函數返回的是的時候,從這個之后的所有的注冊函數都應該注冊在新返回的上。直到遇到下一個回調函數的返回值也是。
Promise
手寫一個PromiseA+的實現。注意這里只是模擬,實際上原生的promise在事件隊列中屬于microTask。這里用setTimeout模擬不是特別恰當。因為setTimeout是一個macroTask。
1. 最簡單的基本功能/** * 定義Promise * 先實現一個最簡單的。用setTimeout模擬一個異步的請求。 */ function Promise(fn){ var value= null; var callbacks = []; this.then = function(onFulfilled) { callbacks.push(onFulfilled); } function resolve(value){ callbacks.forEach(function(cb){ cb(value); }) } fn(resolve); } // 使用Promise var p = new Promise(function(resolve){ setTimeout(function(){ resolve("這是響應的數據") },2000) }) p.then(function(response){ console.log(response); })2.鏈式調用
/** * 先看一下前一個例子存在的問題 * 1.在前一個例子中不斷調用then需要支持鏈式調用,每次執行then都要返回調用對象本身。 * 2.在前一個例子中,當鏈式調用的時候,每次then中的值都是同一個值,這是有問題的。其實第一次then中的返回值,應該是第二次調用then中的函數的參數,依次類推。 * 所以,我們進一步優化一下代碼。 * */ function Promise(fn){ var value= null; var callbacks = []; this.then = function(onFulfilled) { callbacks.push({f:onFulfilled}); return this; } function resolve(value){ callbacks.map(function(cb,index){ if(index === 0){ callbacks[index].value = value; } var rsp = cb.f(cb.value); if(typeof callbacks[index+1] !== "undefined"){ callbacks[index+1].value = rsp; } }) } fn(resolve); } // 使用Promise var p = new Promise(function(resolve){ setTimeout(function(){ resolve("這是響應的數據") },2000) }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); })3. 異步
/** * 先看一下前一個例子存在的問題 * 1. 如果在then方法注冊回調之前,resolve函數就執行了,怎么辦?比如 new Promise的時候傳入的函數是同步函數的話, * then還沒被注冊,resolve就執行了。。這在PromiseA+規范中是不允許的,規范明確要求回調需要通過異步的方式執行。 * 用來保證一致可靠的執行順序。 * * 因此我們需要加入一些處理。把resolve里的代碼放到異步隊列中去。這里我們利用setTimeout來實現。 * 原理就是通過setTimeout機制,將resolve中執行回調的邏輯放置到JS任務隊列末尾,以保證在resolve執行時, * then方法的回調函數已經注冊完成 * */ function Promise(fn){ var value= null; var callbacks = []; this.then = function(onFulfilled) { callbacks.push({f:onFulfilled}); return this; } function resolve(value){ setTimeout(function(){ callbacks.map(function(cb,index){ if(index === 0){ callbacks[index].value = value; } var rsp = cb.f(cb.value); if(typeof callbacks[index+1] !== "undefined"){ callbacks[index+1].value = rsp; } }) },0) } fn(resolve); } // 使用Promise,現在即使是同步的立馬resolve,也能正常運行了。 var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); })4. 狀態機制
/** * 先看一下前一個例子存在的問題 * 1.前一個例子還存在一些問題,如果Promise異步操作已經成功,在這之前注冊的所有回調都會執行, * 但是在這之后再注冊的回調函數就再也不執行了。具體的運行下面這段代碼,可以看到“can i invoke”并沒有打印出來 * 想要解決這個問題,我們就需要加入狀態機制了。具體實現看本文件夾下的另一個js文件里的代碼。 * */ function Promise(fn){ var value= null; var callbacks = []; this.then = function(onFulfilled) { callbacks.push({f:onFulfilled}); return this; } function resolve(value){ setTimeout(function(){ callbacks.map(function(cb,index){ if(index === 0){ callbacks[index].value = value; } var rsp = cb.f(cb.value); if(typeof callbacks[index+1] !== "undefined"){ callbacks[index+1].value = rsp; } }) },0) } fn(resolve); } // var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); }) },0)
/** * 在promise01.js中,我們已經分析了,我們需要加入狀態機制 * 在這里實現一下PromiseA+中關于狀態的規范。 * * Promises/A+規范中的2.1Promise States中明確規定了,pending可以轉化為fulfilled或rejected并且只能轉化一次, * 也就是說如果pending轉化到fulfilled狀態,那么就不能再轉化到rejected。 * 并且fulfilled和rejected狀態只能由pending轉化而來,兩者之間不能互相轉換 * */ function Promise(fn){ var status = "pending" var value= null; var callbacks = []; this.then = function(onFulfilled) { // 如果是pending狀態,則加入到注冊隊列中去。 if(status === "pending"){ callbacks.push({f:onFulfilled}); return this; } // 如果是fulfilled 狀態,此時直接執行傳入的注冊函數即可。 onFulfilled(value); return this; } function resolve(newValue){ value = newValue; status = "fulfilled"; setTimeout(function(){ callbacks.map(function(cb,index){ if(index === 0){ callbacks[index].value = newValue; } var rsp = cb.f(cb.value); if(typeof callbacks[index+1] !== "undefined"){ callbacks[index+1].value = rsp; } }) },0) } fn(resolve); } // var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); }) },1000)
/** * 剛才的例子中,確實打印出了 can i invoke,但是之前then的注冊函數的返回值,并沒有打印出來。 * 也就是說 1 和 2 并沒有被打印出來,看下面的注釋 * */ function Promise(fn){ var status = "pending" var value= null; var callbacks = []; this.then = function(onFulfilled) { if(status === "pending"){ callbacks.push({f:onFulfilled}); return this; } onFulfilled(value); return this; } function resolve(newValue){ value = newValue; status = "fulfilled"; setTimeout(function(){ callbacks.map(function(cb,index){ if(index === 0){ callbacks[index].value = newValue; } var rsp = cb.f(cb.value); if(typeof callbacks[index+1] !== "undefined"){ callbacks[index+1].value = rsp; } }) },0) } fn(resolve); } var p = new Promise(function(resolve){ resolve("aaaaaa") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); // 這里應該打印的是45行返回的1,但是打印出來的確是aaaaaa return 2; }).then(function(response){ console.log(response); // 這里應該打印的是48行返回的2,但是打印出來的確是aaaaaa }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); }) },1000) /** * 問題的根源在于什么呢? * 問題的根源是每次的then的返回值都是p,當狀態是fulfilled,執行的是onFulfilled(value) * 此處的value是p的value,也就是fulfilled狀態的value。根據規范,promise應該是只能發射單值。 * 而我們設計了一個callback堆棧中有一系列的值。生生的把promise變成了多值發射。 * * 所以,調整思路,每個then都應該返回一個promise,這個promise應該是一個全新的promise。 * 具體實現見下一個例子。 */
/** * 根據剛才的分析,我們重新優化一下代碼 * 1.去掉之前的多值設計 * 2.每次的then 返回的都是一個全新的promise * */ function Promise(fn){ var status = "pending" var value= null; var callbacks = []; var self = this; this.then = function(onFulfilled) { return new Promise(function(resolve){ function handle(value){ var res = typeof onFulfilled === "function" ? onFulfilled(value) : value; resolve(res); } // 如果是pending狀態,則加入到注冊隊列中去。 if(status === "pending"){ callbacks.push(handle); // 如果是fulfilled 狀態。 }else if(status === "fulfilled"){ handle(value); } }) } function resolve(newValue){ value = newValue; status = "fulfilled"; setTimeout(function(){ callbacks.map(function(cb){ cb(value); }) },0) }; fn(resolve); } // var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); }) },1000) /** * 運行一下,完美輸出 * 先是輸出“這是響應的數據”,然后是“1”,然后是“2”, 然后是“can i invoke?” * * 接下來我們要好好整理一下代碼了。把一些公用的方法放到構造函數的原型上去。改造之后的例子見下一個例子 */
/** * 根據剛才的分析,我們重新優化一下代碼 * 1.把私有屬性掛到實例上去 * 2.把公共方法掛到構造函數的原型上去 * */ function Promise(fn){ this.status = "pending"; this.value= null; this.callbacks = []; var self = this; function resolve(newValue){ self.value = newValue; self.status = "fulfilled"; setTimeout(function(){ self.callbacks.map(function(cb){ cb(value); }) },0) } fn(resolve); } Promise.prototype = Object.create(null); Promise.prototype.constructor = Promise; Promise.prototype.then = function(onFulfilled){ var self = this; return new Promise(function(resolve){ function handle(value){ var res = typeof onFulfilled === "function"? onFulfilled(value) : value; resolve(res); } if(self.status==="pending"){ self.callbacks.push(handle); }else if(self.status ==="fulfilled"){ handle(self.value); } }) } // 使用 var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return 1; }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); }) },1000)5.處理注冊的函數返回值是promise的情況
/** * 不出意料,又要拋出問題了。當then注冊的回調函數返回的是promise的時候,從這個then之后的所有then的注冊函數 * 都應該注冊在新返回的promise上。直到遇到下一個回調函數的返回值也是promise。 * * 實現思路: * 在handle中判斷注冊函數返回的是否是promise。如果是的話,則resolve這個返回的promise的值,具體代碼看一下36到38行 * */ function Promise(fn){ this.status = "pending"; this.value= null; this.callbacks = []; var self = this; function resolve(newValue){ self.value = newValue; self.status = "fulfilled"; setTimeout(function(){ self.callbacks.map(function(cb){ cb(value); }) },0) } fn(resolve); } Promise.prototype = Object.create(null); Promise.prototype.constructor = Promise; Promise.prototype.then = function(onFulfilled){ var self = this; var promise = new Promise(function(resolve){ function handle(value){ var res = typeof onFulfilled === "function"? onFulfilled(value) : value; if(res instanceof Promise){ promise = res; resolve(res.value); }else { resolve(res); } } if(self.status==="pending"){ self.callbacks.push(handle); }else if(self.status ==="fulfilled"){ handle(self.value); } }) return promise; } // 使用 var p = new Promise(function(resolve){ resolve("這是響應的數據") }) p.then(function(response){ console.log(response); return new Promise(function(resolve){ resolve("testtest") }) }).then(function(response){ console.log(response); return 2; }).then(function(response){ console.log(response); }) setTimeout(function(){ p.then(function(response){ console.log("can i invoke?"); return new Promise(function(resolve){ resolve("hhhhhh") }) }).then(function(response){ console.log(response); }) },1000)
源碼全部在github上:https://github.com/JesseZhao1...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95209.html
摘要:嗝首先,我們通過字面可以看出來是一種解決方案,而且還有兩種傳統的解決方案回調函數和事件,,那么我們就來先聊聊這兩種方案。 前言 雖然今年已經18年,但是今天還是要繼續聊聊ES6的東西,ES6已經過去幾年,可是我們對于ES6的語法究竟是掌握了什么程度,是了解?會用?還是精通?相信大家和我一樣都對自己有著一個提升的心,對于新玩具可不能僅僅了解,對于其中的思想才是最吸引人的,所以接下來會通過...
摘要:今天我們來自己手寫一個符合規范的庫。是異步編程的一種解決方案,比傳統的解決方案回調函數和事件更合理和更強大。我們可以看到,其實就是一個構造函數。所以說我們的數組里存的是一個一個的的回調函數,也就是一個一個。 今天我們來自己手寫一個符合PromiseA+規范的Promise庫。大家是不是很激動呢?? showImg(https://segmentfault.com/img/bV6t4Z?...
摘要:以上代碼,可以完美通過所有用例。在的函數中,為何需要這個同樣是因為規范中明確表示因此我們需要這樣的來確保只會執行一次。其他情況,直接返回以該值為成功狀態的對象。 Promise是前端面試中的高頻問題,我作為面試官的時候,問Promise的概率超過90%,據我所知,大多數公司,都會問一些關于Promise的問題。如果你能根據PromiseA+的規范,寫出符合規范的源碼,那么我想,對于面試...
摘要:被觀察者管理內部和的狀態轉變,同時通過構造函數中傳遞的和方法以主動觸發狀態轉變和通知觀察者。第一個回調函數是對象的狀態變為時調用,第二個回調函數是對象的狀態變為時調用可選實現主要實現第一步,初步構建。 Promise 含義 Promise是異步編程的一種解決方案,比傳統的解決方案(回調函數和事件)更合合理、強大。所謂Promise,簡單來說就是一個容器,里面保存著某個未來才會結束的事件...
摘要:簡單實現前言你可能知道,的任務執行的模式有兩種同步和異步。你已經實現了方法方法是一個很好用的方法。感興趣的朋友可以自行去研究哈附上代碼完整的實現個人博客鏈接 Promise 簡單實現 前言 你可能知道,javascript 的任務執行的模式有兩種:同步和異步。 異步模式非常重要,在瀏覽器端,耗時很長的操作(例如 ajax 請求)都應該異步執行,避免瀏覽器失去響應。 在異步模式編程中,我...
閱讀 2958·2021-11-08 13:20
閱讀 1031·2021-09-22 15:20
閱讀 660·2019-08-30 15:53
閱讀 1964·2019-08-30 15:43
閱讀 1278·2019-08-29 17:21
閱讀 540·2019-08-29 12:15
閱讀 2375·2019-08-28 17:51
閱讀 3142·2019-08-26 13:26