摘要:嗝首先,我們通過(guò)字面可以看出來(lái)是一種解決方案,而且還有兩種傳統(tǒng)的解決方案回調(diào)函數(shù)和事件,,那么我們就來(lái)先聊聊這兩種方案。
前言
雖然今年已經(jīng)18年,但是今天還是要繼續(xù)聊聊ES6的東西,ES6已經(jīng)過(guò)去幾年,可是我們對(duì)于ES6的語(yǔ)法究竟是掌握了什么程度,是了解?會(huì)用?還是精通?相信大家和我一樣都對(duì)自己有著一個(gè)提升的心,對(duì)于新玩具可不能僅僅了解,對(duì)于其中的思想才是最吸引人的,所以接下來(lái)會(huì)通過(guò)一篇文章,來(lái)讓大家對(duì)于Promise這個(gè)玩具做到精通的程度?。。?/p>
打開(kāi)一瓶冰闊落~~~
PromisePromise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6將其寫(xiě)進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對(duì)象。
嗝~
首先,我們通過(guò)字面可以看出來(lái)Pormise是一種解決方案,而且還有兩種傳統(tǒng)的解決方案·回調(diào)函數(shù)和事件,ok,那么我們就來(lái)先聊聊這兩種方案。
回調(diào)函數(shù) Callback回調(diào)函數(shù)想必大家都不陌生,就是我們常見(jiàn)的把一個(gè)函數(shù)當(dāng)做參數(shù)傳遞給另外一個(gè)函數(shù),在滿(mǎn)足了一定的條件之后再去執(zhí)行回調(diào),比如我們想要實(shí)現(xiàn)一個(gè)在三秒后去計(jì)算1到5的和,那么:
// 求和函數(shù) function sum () { return eval([...arguments].join("+")) } // 三秒后執(zhí)行函數(shù) function asycnGetSum (callback) { setTimeout(function(){ var result = callback(1,2,3,4,5); console.log(result) },3000) } asyncGetSum(sum);
這樣的實(shí)現(xiàn)就是回調(diào)函數(shù),但是如果我要實(shí)現(xiàn)在一段動(dòng)畫(huà),動(dòng)畫(huà)的執(zhí)行過(guò)程是小球先向右移動(dòng)100px,然后再向下移動(dòng)100px,在向左移動(dòng)100px,每段動(dòng)畫(huà)持續(xù)時(shí)間都是3s.
dom.animate({left:"100px"},3000,"linear",function(){ dom.animate({top:"100px"},3000,"linear",function(){ dom.animate({left:"0px"},3000,"linear",function(){ console.log("動(dòng)畫(huà) done") }) }) })
這樣就會(huì)看到形成了一個(gè)回調(diào)嵌套,也就是我們常說(shuō)的回調(diào)地獄,導(dǎo)致代碼可讀性十分差。
事件事件處理就是jQuery中的on綁定事件和trigger觸發(fā)事件,其實(shí)就是我們常見(jiàn)的發(fā)布訂閱模式,當(dāng)我訂閱了一個(gè)事件,那么我就是訂閱者,如果發(fā)布者發(fā)布了數(shù)據(jù)之后,那么我就要收到相應(yīng)的通知。
// 定義一個(gè)發(fā)布中心 let publishCenter = { subscribeArrays:{}, // 定義一個(gè)訂閱者回調(diào)函數(shù)callback subscribe:function(key,callback){ // 增加訂閱者 if(!this.subscribeArrays[key]){ this.subscribeArrays[key] = []; } this.subscribeArrays[key].push(callback) }, publish:function(){ //發(fā)布 第一個(gè)參數(shù)是key let params = [...arguments]; let key = params.shift(); let callbacks = this.subscribeArrays[key]; if(!callbacks || callbacks.length === 0){ // 如果沒(méi)人訂閱 那么就返回 return false } for( let i = 0 ; i < callbacks.length; i++ ){ callbacks[i].apply( this, params ); } } }; // 訂閱 一個(gè)wantWatermelon事件 publishCenter.subscribe("wantWatermelon",function(){console.log("恰西瓜咯~~")}) //觸發(fā)wantWatermelon事件 好咯 可以看到 恰西瓜咯 publishCenter.publish("wantWatermelon")
恰西瓜中~~~
Promise A+嗝~ok,吃完我們進(jìn)入正題,看到上面異步編程如此如此如此麻煩,對(duì)于我這種頭大用戶(hù),當(dāng)然是拒絕的啊,還好我們有Pormise(Pormise大法好),下面我們就來(lái)通過(guò)實(shí)現(xiàn)一個(gè)Promise去更深的了解Promise的原理,首先我們了解一下PromiseA+,它是一種規(guī)范,用來(lái)約束大家寫(xiě)的Promise方法的,為了讓大家寫(xiě)的Promise杜絕一些錯(cuò)誤,按照我們所期望的流程來(lái)走,因此就出現(xiàn)了PromiseA+規(guī)范。
Promise特點(diǎn)我們根據(jù)PromiseA+文檔來(lái)一步一步的看Promise有什么特點(diǎn)。
首先我們看文檔的2.1節(jié),題目是Promise states,也就是說(shuō)講的是Promise的狀態(tài),那么都說(shuō)了些什么呢,我們來(lái)看一哈:
一個(gè)promise只有三種狀態(tài),pending態(tài),fulfilled態(tài)(完成態(tài)),rejected(拒絕態(tài))
當(dāng)promise處于pending態(tài)時(shí),可能轉(zhuǎn)化成fulfilled或者rejected
一旦promise的狀態(tài)改成了fulfilled后,狀態(tài)就不能再改變了,并且需要提供一個(gè)不可變的value
一旦promise的狀態(tài)改成了rejected后,狀態(tài)就不能再改變了,并且需要提供一個(gè)不可變的reason
ok,那么我們就開(kāi)始寫(xiě)我們自己的Promise,我們先看看一段正常Promise的寫(xiě)法
// 成功或者失敗是需要提供一個(gè)value或者reason let promise1 = new Promise((resolve,rejected)=>{ // 可以發(fā)現(xiàn) 當(dāng)我們new Promise的時(shí)候這句話(huà)是同步執(zhí)行的 也就是說(shuō)當(dāng)我們初始化一個(gè)promise的時(shí)候 內(nèi)部的回調(diào)函數(shù)(通常我們叫做執(zhí)行器executor)會(huì)立即執(zhí)行 console.log("hahahha"); // promise內(nèi)部支持異步 setTimeout(function(){ resolve(123); },100) // throw new Error("error") 我們也可以在執(zhí)行器內(nèi)部直接拋出一個(gè)錯(cuò)誤 這時(shí)promise會(huì)直接變成rejected態(tài) })
根據(jù)我們上面的代碼還有PromiseA+規(guī)范中的狀態(tài)說(shuō)明,我們可以知道Promise已經(jīng)有了下面幾個(gè)特點(diǎn)
promise有三種狀態(tài) 默認(rèn)pending態(tài) pending可以變成fulfilled(成功態(tài))或者rejected(失敗態(tài)),而一旦轉(zhuǎn)變之后就不能在變成其他值了
promise內(nèi)部有一個(gè)value 用來(lái)存儲(chǔ)成功態(tài)的結(jié)果
promise內(nèi)部有一個(gè)reason 用來(lái)存儲(chǔ)失敗態(tài)的原因
promise接受一個(gè)executor函數(shù),這個(gè)函數(shù)有兩個(gè)參數(shù),一個(gè)是resolve方法,一個(gè)是reject方法,當(dāng)執(zhí)行resolve時(shí),promise狀態(tài)改變?yōu)?b>fulfilled,執(zhí)行reject時(shí),promise狀態(tài)改變?yōu)?b>rejected
默認(rèn) new Promise 執(zhí)行的時(shí)候內(nèi)部的executor函數(shù)執(zhí)行
promise內(nèi)部支持異步改變狀態(tài)
promise內(nèi)部支持拋出異常,那么該promise的狀態(tài)直接改成rejected
我們接下來(lái)繼續(xù)看PromiseA+文檔:
promise必須要有一個(gè)then方法,用來(lái)訪(fǎng)問(wèn)它當(dāng)前的value或者是reason
該方法接受兩個(gè)參數(shù)onFulfilled(成功回掉函數(shù)),onRejected(失敗回調(diào)函數(shù)) promise.then(onFulfilled, onRejected)
這兩個(gè)參數(shù)都是可選參數(shù),如果發(fā)現(xiàn)這兩個(gè)參數(shù)不是函數(shù)類(lèi)型的話(huà),那么就忽略 比如 promise.then().then(data=>console.log(data),err=>console.log(err)) 就可以形成一個(gè)值穿透
onFulfilled必須在promise狀態(tài)改成fulfilled之后改成調(diào)用,并且呢promise內(nèi)部的value值是這個(gè)函數(shù)的參數(shù),而且這個(gè)函數(shù)不能重復(fù)調(diào)用
onRejected必須在promise狀態(tài)改成rejected之后改成調(diào)用,并且呢promise內(nèi)部的reason值是這個(gè)函數(shù)的參數(shù),而且這個(gè)函數(shù)不能重復(fù)調(diào)用
onFulfilled和onRejected這兩個(gè)方法必須要在當(dāng)前執(zhí)行棧的上下文執(zhí)行完畢后再調(diào)用,其實(shí)就是事件循環(huán)中的微任務(wù)(setTimeout是宏任務(wù),有一定的差異)
onFulfilled和onRejected這兩個(gè)方法必須通過(guò)函數(shù)調(diào)用,也就是說(shuō) 他們倆不是通過(guò)this.onFulfilled()或者this.onRejected()調(diào)用,直接onFulfilled()或者onRejected()
then方法可以在一個(gè)promise上多次調(diào)用,也就是我們常見(jiàn)的鏈?zhǔn)秸{(diào)用
如果當(dāng)前promise的狀態(tài)改成了fulfilled那么就要按照順序依次執(zhí)行then方法中的onFulfilled回調(diào)
如果當(dāng)前promise的狀態(tài)改成了rejected那么就要按照順序依次執(zhí)行then方法中的onRejected回調(diào)
then方法必須返回一個(gè)promise(接下來(lái)我們會(huì)把這個(gè)promise稱(chēng)做promise2),類(lèi)似于 promise2 = promise1.then(onFulfilled, onRejected);
如果呢onFulfilled()或者onRejected()任一一個(gè)返回一個(gè)值x,那么就要去執(zhí)行resolvePromise這個(gè)函數(shù)中去(這個(gè)函數(shù)是用來(lái)處理返回值x遇到的各種值,然后根據(jù)這些值去決定我們剛剛then方法中onFulfilled()或者onRejected()這兩個(gè)回調(diào)返回的promise2的狀態(tài))
如果我們?cè)?b>then中執(zhí)行onFulfilled()或者onRejected()方法時(shí)產(chǎn)生了異常,那么就將promise2用異常的原因e去reject
如果onFulfilled或者onRejected不是函數(shù),并且promise的狀態(tài)已經(jīng)改成了fulfilled或者rejected,那么就用同樣的value或者reason去更新promise2的狀態(tài)(其實(shí)這一條和第三條一個(gè)道理,也就是值得穿透問(wèn)題)
好吧,我們總結(jié)了這么多規(guī)范特點(diǎn),那么我們就用這些先來(lái)練練手
/** * 實(shí)現(xiàn)一個(gè)PromiseA+ * @description 實(shí)現(xiàn)一個(gè)簡(jiǎn)要的promise * @param {Function} executor 執(zhí)行器 * @author Leslie */ function Promise(executor){ let self = this; self.status = "pending"; // 存儲(chǔ)promise狀態(tài) pending fulfilled rejected. self.value = undefined; // 存儲(chǔ)成功后的值 self.reason = undefined; // 記錄失敗的原因 self.onfulfilledCallbacks = []; // 異步時(shí)候收集成功回調(diào) self.onrejectedCallbacks = []; // 異步時(shí)候收集失敗回調(diào) function resolve(value){ if(self.status === "pending"){ self.status = "fulfilled";// resolve的時(shí)候改變promise的狀態(tài) self.value = value;//修改成功的值 // 異步執(zhí)行后 調(diào)用resolve 再把存儲(chǔ)的then中的成功回調(diào)函數(shù)執(zhí)行一遍 self.onfulfilledCallbacks.forEach(element => { element() }); } } function reject(reason){ if(self.status === "pending"){ self.status = "rejected";// reject的時(shí)候改變promise的狀態(tài) self.reason = reason; // 修改失敗的原因 // 異步執(zhí)行后 調(diào)用reject 再把存儲(chǔ)的then中的失敗回調(diào)函數(shù)執(zhí)行一遍 self.onrejectedCallbacks.forEach(element => { element() }); } } // 如果執(zhí)行器中拋出異常 那么就把promise的狀態(tài)用這個(gè)異常reject掉 try { //執(zhí)行 執(zhí)行器 executor(resolve,reject); } catch (error) { reject(error) } } Promise.prototype.then = function(onfulfilled,onrejected){ // onfulfilled then方法中的成功回調(diào) // onrejected then方法中的失敗回調(diào) let self = this; // 如果onfulfilled不是函數(shù) 那么就用默認(rèn)的函數(shù)替代 以便達(dá)到值穿透 onfulfilled = typeof onfulfilled === "function"?onfulfilled:val=>val; // 如果onrejected不是函數(shù) 那么就用默認(rèn)的函數(shù)替代 以便達(dá)到值穿透 onrejected = typeof onrejected === "function"?onrejected: err=>{throw err} let promise2 = new Promise((resolve,reject)=>{ if(self.status === "fulfilled"){ // 加入setTimeout 模擬異步 // 如果調(diào)用then的時(shí)候promise 的狀態(tài)已經(jīng)變成了fulfilled 那么就調(diào)用成功回調(diào) 并且傳遞參數(shù)為 成功的value setTimeout(function(){ // 如果執(zhí)行回調(diào)發(fā)生了異常 那么就用這個(gè)異常作為promise2的失敗原因 try { // x 是執(zhí)行成功回調(diào)的結(jié)果 let x = onfulfilled(self.value); // 調(diào)用resolvePromise函數(shù) 根據(jù)x的值 來(lái)決定promise2的狀態(tài) resolvePromise(promise2,x,resolve,reject); } catch (error) { reject(error) } },0) } if(self.status === "rejected"){ // 加入setTimeout 模擬異步 // 如果調(diào)用then的時(shí)候promise 的狀態(tài)已經(jīng)變成了rejected 那么就調(diào)用失敗回調(diào) 并且傳遞參數(shù)為 失敗的reason setTimeout(function(){ // 如果執(zhí)行回調(diào)發(fā)生了異常 那么就用這個(gè)異常作為promise2的失敗原因 try { // x 是執(zhí)行失敗回調(diào)的結(jié)果 let x = onrejected(self.reason); // 調(diào)用resolvePromise函數(shù) 根據(jù)x的值 來(lái)決定promise2的狀態(tài) resolvePromise(promise2,x,resolve,reject); } catch (error) { reject(error) } },0) } if(self.status === "pending"){ //如果調(diào)用then的時(shí)候promise的狀態(tài)還是pending,說(shuō)明promsie執(zhí)行器內(nèi)部的resolve或者reject是異步執(zhí)行的,那么就需要先把then方法中的成功回調(diào)和失敗回調(diào)存儲(chǔ)襲來(lái),等待promise的狀態(tài)改成fulfilled或者rejected時(shí)候再按順序執(zhí)行相關(guān)回調(diào) self.onfulfilledCallbacks.push(()=>{ //setTimeout模擬異步 setTimeout(function(){ // 如果執(zhí)行回調(diào)發(fā)生了異常 那么就用這個(gè)異常作為promise2的失敗原因 try { // x 是執(zhí)行成功回調(diào)的結(jié)果 let x = onfulfilled(self.value) // 調(diào)用resolvePromise函數(shù) 根據(jù)x的值 來(lái)決定promise2的狀態(tài) resolvePromise(promise2,x,resolve,reject); } catch (error) { reject(error) } },0) }) self.onrejectedCallbacks.push(()=>{ //setTimeout模擬異步 setTimeout(function(){ // 如果執(zhí)行回調(diào)發(fā)生了異常 那么就用這個(gè)異常作為promise2的失敗原因 try { // x 是執(zhí)行失敗回調(diào)的結(jié)果 let x = onrejected(self.reason) // 調(diào)用resolvePromise函數(shù) 根據(jù)x的值 來(lái)決定promise2的狀態(tài) resolvePromise(promise2,x,resolve,reject); } catch (error) { reject(error) } },0) }) } }) return promise2; }
一氣呵成,是不是覺(jué)得之前總結(jié)出的特點(diǎn)十分有效,對(duì)著特點(diǎn)十分順暢的就擼完了代碼~
那么就讓我們接著來(lái)看看promiseA+文檔里還有些什么內(nèi)容吧
resolvePromise這個(gè)函數(shù)呢會(huì)決定promise2用什么樣的狀態(tài),如果x是一個(gè)普通值,那么就直接采用x,如果x是一個(gè)promise那么就將這個(gè)promise的狀態(tài)當(dāng)成是promise2的狀態(tài)
判斷如果x和promise2是一個(gè)對(duì)象,即promise2 === x,那么就陷入了循環(huán)調(diào)用,這時(shí)候promise2就會(huì)以一個(gè)TypeError為reason轉(zhuǎn)化為rejected
如果x是一個(gè)promise,那么promise2就采用x的狀態(tài),用和x相同的value去resolve,或者用和x相同的reason去reject
如果x是一個(gè)對(duì)象或者是函數(shù) 那么就先執(zhí)行let then = x.then
如果x不是一個(gè)對(duì)象或者函數(shù) 那么就resolve 這個(gè)x
如果在執(zhí)行上面的語(yǔ)句中報(bào)錯(cuò)了,那么就用這個(gè)錯(cuò)誤原因去reject promise2
如果then是一個(gè)函數(shù),那么就執(zhí)行then.call(x,resolveCallback,rejectCallback)
如果then不是一個(gè)函數(shù),那么就resolve這個(gè)x
如果x是fulfilled態(tài) 那么就會(huì)走resolveCallback這個(gè)函數(shù),這時(shí)候就默認(rèn)把成功的value作為參數(shù)y傳遞給resolveCallback,即y=>resolvePromise(promise2,y),繼續(xù)調(diào)用resolvePromise這個(gè)函數(shù) 確保 返回值是一個(gè)普通值而不是promise
如果x是rejected態(tài) 那么就把這個(gè)失敗的原因reason作為promise2的失敗原因reject出去
如果resolveCallback,rejectCallback這兩個(gè)函數(shù)已經(jīng)被調(diào)用了,或者多次被相同的參數(shù)調(diào)用,那么就確保只調(diào)第一次,剩下的都忽略掉
如果調(diào)用then拋出異常了,并且如果resolveCallback,rejectCallback這兩個(gè)函數(shù)已經(jīng)被調(diào)用了,那么就忽略這個(gè)異常,否則就用這個(gè)異常作為promise2的reject原因
我們又又又又又又總結(jié)了這么多,好吧不說(shuō)了總結(jié)多少就開(kāi)擼吧。
/** * 用來(lái)處理then方法返回結(jié)果包裝成promise 方便鏈?zhǔn)秸{(diào)用 * @param {*} promise2 then方法執(zhí)行產(chǎn)生的promise 方便鏈?zhǔn)秸{(diào)用 * @param {*} x then方法執(zhí)行完成功回調(diào)或者失敗回調(diào)后的result * @param {*} resolve 返回的promise的resolve方法 用來(lái)更改promise最后的狀態(tài) * @param {*} reject 返回的promise的reject方法 用來(lái)更改promise最后的狀態(tài) */ function resolvePromise(promise2,x,resolve,reject){ // 首先判斷x和promise2是否是同一引用 如果是 那么就用一個(gè)類(lèi)型錯(cuò)誤作為Promise2的失敗原因reject if( promise2 === x) return reject(new TypeError("typeError:大佬,你循環(huán)引用了!")); // called 用來(lái)記錄promise2的狀態(tài)改變,一旦發(fā)生改變了 就不允許 再改成其他狀態(tài) let called; if( x !== null && ( typeof x === "object" || typeof x === "function")){ // 如果x是一個(gè)對(duì)象或者函數(shù) 那么他就有可能是promise 需要注意 null typeof也是 object 所以需要排除掉 //先獲得x中的then 如果這一步發(fā)生異常了,那么就直接把異常原因reject掉 try { let then = x.then;//防止別人瞎寫(xiě)報(bào)錯(cuò) if(typeof then === "function"){ //如果then是個(gè)函數(shù) 那么就調(diào)用then 并且把成功回調(diào)和失敗回調(diào)傳進(jìn)去,如果x是一個(gè)promise 并且最終狀態(tài)時(shí)成功,那么就會(huì)執(zhí)行成功的回調(diào),如果失敗就會(huì)執(zhí)行失敗的回調(diào)如果失敗了,就把失敗的原因reject出去,做為promise2的失敗原因,如果成功了那么成功的value時(shí)y,這個(gè)y有可能仍然是promise,所以需要遞歸調(diào)用resolvePromise這個(gè)方法 直達(dá)返回值不是一個(gè)promise then.call(x,y => { if(called) return; called = true; resolvePromise(promise2,y,resolve,reject) }, error=>{ if(called) return called = true; reject(error) }) }else{ resolve(x) } } catch (error) { if(called) return called = true; reject(error) } }else{ // 如果是一個(gè)普通值 那么就直接把x作為promise2的成功value resolve掉 resolve(x) } }
finnnnnnnnnally,我們終于通過(guò)我們的不懈努力實(shí)現(xiàn)了一個(gè)基于PromiseA+規(guī)范的Promise!
最后呢為了完美,我們還要在這個(gè)promise上實(shí)現(xiàn)Promise.resolve,Promise.reject,以及catch,Promise.all和Promise.race這些方法。
Promise的一些方法Promise.resolve = function(value){ return new Promise((resolve,reject)=>{ resolve(value) }) } Promise.reject = function(reason){ return new Promise((resolve,reject)=>{ reject(reason) }) } Promise.prototype.catch = function(onRejected){ return this.then(null,onRejected) } Promise.all = function(promises){ return new Promise((resolve,reject)=>{ let arr = []; let i = 0; function getResult(index,value){ arr[index] = value; if(++i == promises.length) { resolve(arr) } } for(let i = 0;iPromise 語(yǔ)法糖{ getResult(i,data) },reject) } }) } Promise.race = function(promises){ return new Promise((resolve,reject)=>{ for(let i = 0 ; i < promises.length ; i++){ promises[i].then(resolve,reject) } }) }
恰完西瓜來(lái)口糖,語(yǔ)法糖是為了讓我們書(shū)寫(xiě)promise的時(shí)候能夠更加的快速,所以做了一層改變,我們來(lái)看一個(gè)例子,比如當(dāng)我們封裝一個(gè)異步讀取圖片的寬高函數(shù)
// 原來(lái)的方式 let getImgWidthHeight = function(imgUrl){ return new Promise((resolve,reject)=>{ let img = new Image(); img.onload = function(){ resolve(img.width+"-"+img.height) } img.onerror = function(e){ reject(e) } img.src = imgUrl; }) }
是不是覺(jué)得怎么寫(xiě)起來(lái)有點(diǎn)舒服但又有點(diǎn)不舒服,好像我每次都要去寫(xiě)執(zhí)行器啊!為什么!好的,沒(méi)有為什么,既然不舒服 我們就改!
// 實(shí)現(xiàn)一個(gè)promise的語(yǔ)法糖 Promise.defer = Promise.deferred = function (){ let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }) return dfd }
有了上面的語(yǔ)法糖我們?cè)倏匆幌履莻€(gè)圖片的函數(shù)怎么寫(xiě)
let newGetImgWidthHeight = function(imgUrl){ let dfd = Promise.defer(); let img = new Image(); img.onload = function(){ dfd.resolve(img.width+"-"+img.height) } img.onerror = function(e){ dfd.reject(e) } img.url = imgUrl; return dfd.promise }
是不是發(fā)現(xiàn)我們少了一層函數(shù)嵌套,呼 得勁
npm install promises-aplus-tests -g
既然我們都說(shuō)了我們是遵循promiseA+規(guī)范的,那至少要拿出點(diǎn)證據(jù)來(lái)是不是,不然是不是說(shuō)服不了大家,那么我們就用promises-aplus-tests這個(gè)包來(lái)檢測(cè)我們寫(xiě)的promise究竟怎么樣呢!安裝完成之后來(lái)跑一下我們的promise
最終跑出來(lái)我們?nèi)客ㄟ^(guò)測(cè)試!酷!晚餐再加個(gè)雞腿~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/96324.html
摘要:周五就想寫(xiě)這篇文章,但是無(wú)奈花花世界的誘惑太多就一直拖到了今天,自責(zé)遍進(jìn)入正題對(duì)象用于表示一個(gè)異步操作的最終狀態(tài)完成或失敗,以及其返回的值。 周五就想寫(xiě)這篇文章,但是無(wú)奈花花世界的誘惑太多……就一直拖到了今天,自責(zé)1e4遍;進(jìn)入正題Promise: Promise 對(duì)象用于表示一個(gè)異步操作的最終狀態(tài)(完成或失敗),以及其返回的值。 上為MDNPromise的定義;ES6規(guī)定Promis...
你有遇見(jiàn)過(guò)給bind返回的函數(shù)做new操作的場(chǎng)景,本篇主要講述的就是實(shí)現(xiàn)一下兼容new操作的bind寫(xiě)法,順便學(xué)習(xí)一下new操作符,為大家提供下參考?! 〈蠹铱梢匀タ聪玛P(guān)于 JS 中 bind 方法的實(shí)現(xiàn)的文章,并給出了實(shí)現(xiàn): Function.prototype.myBind=function(thisArg,...prefixArgs){ constfn=this; return...
摘要:傳入的回調(diào)函數(shù)也不是一個(gè)函數(shù)類(lèi)型,那怎么辦規(guī)范中說(shuō)忽略它就好了。因此需要判斷一下回調(diào)函數(shù)的類(lèi)型,如果明確是個(gè)函數(shù)再執(zhí)行它。 Promise是什么 所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處...
摘要:先說(shuō)下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問(wèn)到相關(guān)問(wèn)題,其他公司壓根沒(méi)問(wèn)。我自己回答的是自己開(kāi)發(fā)組件面臨的問(wèn)題。完全不用擔(dān)心對(duì)方到時(shí)候打電話(huà)核對(duì)的問(wèn)題。 2019的5月9號(hào),離發(fā)工資還有1天的時(shí)候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說(shuō):阿郭,我們公司要倒閉了,錢(qián)是沒(méi)有的啦,為了不耽誤你,你趕緊出去找工作吧。聽(tīng)到這話(huà),我虎軀一震,這已經(jīng)是第2個(gè)月沒(méi)工資了。 公...
閱讀 1359·2023-04-25 15:21
閱讀 2688·2021-11-24 10:23
閱讀 3413·2021-10-11 10:59
閱讀 3262·2021-09-03 10:28
閱讀 1741·2019-08-26 13:45
閱讀 2334·2019-08-26 12:11
閱讀 933·2019-08-26 12:00
閱讀 1720·2019-08-26 10:44