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