国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Promise之你看得懂的Promise

wwolf / 1638人閱讀

摘要:存放成功回調(diào)的函數(shù)存放失敗回調(diào)的函數(shù)監(jiān)聽回調(diào)函數(shù)然后是需要多加一個狀態(tài)判斷,當(dāng)中是異步操作時,需要在我們之前定義的回調(diào)函數(shù)數(shù)組中添加一個回調(diào)函數(shù)。參數(shù)函數(shù)返回的對象,函數(shù)的返回值,最外層的上的和。

本文由作者陳旭鋒(任職網(wǎng)易考拉)授權(quán)網(wǎng)易云社區(qū)發(fā)布。

Promise源碼詳解
學(xué)習(xí)知識要善于思考,思考,再思考。 —— 愛因斯坦

1.回調(diào)地獄
曾幾何時,我們的代碼是這樣的,為了拿到回調(diào)的結(jié)果,不得不callback hell,這種環(huán)環(huán)相扣的代碼可以說是相當(dāng)惡心了

let fs = require("fs")
fs.readFile("./a.txt","utf8",function(err,data){
fs.readFile(data,"utf8",function(err,data){

fs.readFile(data,"utf8",function(err,data){      console.log(data)
})

})
})
終于,我們的蓋世英雄出現(xiàn)了,他身披金甲圣衣、駕著七彩祥云。好吧打岔兒了,沒錯他就是我們的Promise,那讓我們來看看用了Promise之后,上面的代碼會變成什么樣吧

let fs = require("fs")function read(url){ return new Promise((resolve,reject)=>{

fs.readFile(url,"utf8",function(error,data){
  error && reject(error)
  resolve(data)
})

})
}

read("./a.txt").then(data=>{ return read(data)
}).then(data=>{ return read(data)
}).then(data=>{ console.log(data)
})
如上所示

真的是很方便,有木有?意中人可以說是Swag到變形了。那么言歸正傳,我們怎么才能自己寫一個這么Swag的解決異步神器呢?

2.重點開始,小眼睛都看過來
2.1 Promise/A+
首先我們要知道自己手寫一個Promise,應(yīng)該怎么去寫,誰來告訴我們怎么寫,需要遵循什么樣的規(guī)則。當(dāng)然這些你都不用擔(dān)心,其實業(yè)界都是通過一個規(guī)則指標來生產(chǎn)Promise的。讓我們來看看是什么東西。傳送門?Promise/A+

2.2 constructor
我們先聲明一個類,叫做Promise,里面是構(gòu)造函數(shù)。如果es6還有問題的可以去阮大大的博客上學(xué)習(xí)一下(傳送門?es6)

class Promise{
constructor(executor){ //控制狀態(tài),使用了一次之后,接下來的都不被使用

this.status = "pendding"
this.value = undefined
this.reason = undefined

//定義resolve函數(shù)
let resolve = (data)=>{      //這里pendding,主要是為了防止executor中調(diào)用了兩次resovle或reject方法,而我們只調(diào)用一次
  if(this.status==="pendding"){        this.status = "resolve"
    this.value = data
  } 
}    //定義reject函數(shù)
let reject = (data)=>{      if(this.status==="pendding"){        this.status = "reject"        
    this.reason = data
  } 
}    //executor方法可能會拋出異常,需要捕獲
try{      //將resolve和reject函數(shù)給使用者      
  executor(resolve,reject)      
}catch(e){      //如果在函數(shù)中拋出異常則將它注入reject中
  reject(e)
}

}
}
那么接下來我會分析上面代碼的作用,原理

executor:這是實例Promise對象時在構(gòu)造器中傳入的參數(shù),一般是一個function(resolve,reject){}

status:``Promise的狀態(tài),一開始是默認的pendding狀態(tài),每當(dāng)調(diào)用道resolve和reject方法時,就會改變其值,在后面的then方法中會用到

value:resolve回調(diào)成功后,調(diào)用resolve方法里面的參數(shù)值

reason:reject回調(diào)成功后,調(diào)用reject方法里面的參數(shù)值

resolve:聲明resolve方法在構(gòu)造器內(nèi),通過傳入的executor方法傳入其中,用以給使用者回調(diào)

reject:聲明reject方法在構(gòu)造器內(nèi),通過傳入的executor方法傳入其中,用以給使用者回調(diào)

2.3 then
then方法是Promise中最為重要的方法,他的用法大家都應(yīng)該已經(jīng)知道,就是將Promise中的resolve或者reject的結(jié)果拿到,那么我們就能知道這里的then方法需要兩個參數(shù),成功回調(diào)和失敗回調(diào),上代碼!

then(onFufilled,onRejected){
if(this.status === "resolve"){

onFufilled(this.value)

} if(this.status === "reject"){

onRejected(this.reason)

}
}
這里主要做了將構(gòu)造器中resolve和reject的結(jié)果傳入onFufilled和onRejected中,注意這兩個是使用者傳入的參數(shù),是個方法。所以你以為這么簡單就完了?要想更Swag的應(yīng)對各種場景,我們必須得再完善。繼續(xù)往下走!

3.異步的Promise
之前我們只是處理了同步情況下的Promise,簡而言之所有操作都沒有異步的成分在內(nèi)。那么如果是異步該怎么辦?

3.1 callback!!!!
最早處理異步的方法就是callback,就相當(dāng)于我讓你幫我掃地,我會在給你發(fā)起任務(wù)時給你一個手機,之后我做自己的事情去,不用等你,等你掃完地就會打手機給我,誒,我就知道了地掃完了。這個手機就是callback,回調(diào)函數(shù)。

首先我們需要改一下構(gòu)造器里的代碼,分別添加兩個回調(diào)函數(shù)的數(shù)組,分別對應(yīng)成功回調(diào)和失敗回調(diào)。他們的作用是當(dāng)成功執(zhí)行resolve或reject時,執(zhí)行callback。

//存放成功回調(diào)的函數(shù)this.onResolvedCallbacks = []//存放失敗回調(diào)的函數(shù)this.onRejectedCallbacks = []let resolve = (data)=>{ if(this.status==="pendding"){ this.status = "resolve"

this.value = data    //監(jiān)聽回調(diào)函數(shù)
this.onResolvedCallbacks.forEach(fn=>fn())

}
}let reject = (data)=>{ if(this.status==="pendding"){ this.status = "reject"

this.reason = data    this.onRejectedCallbacks.forEach(fn=>fn())

}
}
然后是then需要多加一個狀態(tài)判斷,當(dāng)Promise中是異步操作時,需要在我們之前定義的回調(diào)函數(shù)數(shù)組中添加一個回調(diào)函數(shù)。

if(this.status === "pendding"){ this.onResolvedCallbacks.push(()=>{ // to do....

let x = onFufilled(this.value)
resolvePromise(promise2,x,resolve,reject)

}) this.onRejectedCallbacks.push(()=>{ let x = onRejected(this.reason)

resolvePromise(promise2,x,resolve,reject)

})
}
ok!大功告成,異步已經(jīng)解決了

3.2 resolvePromise
這也是Promise中的重頭戲,我來介紹一下,我們在用Promise的時候可能會發(fā)現(xiàn),當(dāng)then函數(shù)中return了一個值,我們可以繼續(xù)then下去,不過是什么值,都能在下一個then中獲取,還有,當(dāng)我們不在then中放入?yún)?shù),例:promise.then().then(),那么其后面的then依舊可以得到之前then返回的值,可能你現(xiàn)在想很迷惑。讓我來解開你心中的憂愁,follow me。

then(onFufilled,onRejected){

//解決onFufilled,onRejected沒有傳值的問題
onFufilled = typeof onFufilled === "function"?onFufilled:y=>y    //因為錯誤的值要讓后面訪問到,所以這里也要跑出個錯誤,不然會在之后then的resolve中捕獲
onRejected = typeof onRejected === "function"?onRejected:err=>{ throw err ;}    //聲明一個promise對象
let promise2    if(this.status === "resolve"){      //因為在.then之后又是一個promise對象,所以這里肯定要返回一個promise對象
  promise2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{          //因為穿透值的緣故,在默認的跑出一個error后,不能再用下一個的reject來接受,只能通過try,catch        
      try{            //因為有的時候需要判斷then中的方法是否返回一個promise對象,所以需要判斷
        //如果返回值為promise對象,則需要取出結(jié)果當(dāng)作promise2的resolve結(jié)果
        //如果不是,直接作為promise2的resolve結(jié)果
        let x = onFufilled(this.value)            //抽離出一個公共方法來判斷他們是否為promise對象
        resolvePromise(promise2,x,resolve,reject)
      }catch(e){
        reject(e)
      }
    },0)
  })
}    if(this.status === "reject"){
  promise2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{          try{            let x = onRejected(this.reason)
        resolvePromise(promise2,x,resolve,reject)
      }catch(e){
        reject(e)
      }
    },0)
  })
}    if(this.status === "pendding"){
  promise2 = new Promise((resolve,reject)=>{        this.onResolvedCallbacks.push(()=>{          // to do....
      setTimeout(()=>{            try{              let x = onFufilled(this.value)
          resolvePromise(promise2,x,resolve,reject)
        }catch(e){
          reject(e)
        }
      },0)
    })        this.onRejectedCallbacks.push(()=>{
      setTimeout(()=>{            try{              let x = onRejected(this.reason)
          resolvePromise(promise2,x,resolve,reject)
        }catch(e){
          reject(e)
        }
      })
    })
  })
}    return promise2

}
一下子多了很多方法,不用怕,我會一一解釋

返回Promise?:首先我們要注意的一點是,then有返回值,then了之后還能在then,那就說明之前的then返回的必然是個Promise。

為什么外面要包一層setTimeout?:因為Promise本身是一個異步方法,屬于微任務(wù)一列,必須得在執(zhí)行棧執(zhí)行完了在去取他的值,所以所有的返回值都得包一層異步setTimeout。

為什么開頭有兩個判斷?:這就是之前想要解決的如果then函數(shù)中的參數(shù)不是函數(shù),那么我們需要做處理。如果onFufilled不是函數(shù),就需要自定義個函數(shù)用來返回之前resolve的值,如果onRejected不是函數(shù),自定義個函數(shù)拋出異常。這里會有個小坑,如果這里不拋出異常,會在下一個then的onFufilled中拿到值。又因為這里拋出了異常所以所有的onFufilled或者onRejected都需要try/catch,這也是Promise/A+的規(guī)范。當(dāng)然本人覺得成功的回調(diào)不需要拋出異常也可以,大家可以仔細想想。

resolvePromise是什么?:這其實是官方Promise/A+的需求。因為你的then可以返回任何職,當(dāng)然包括Promise對象,而如果是Promise對象,我們就需要將他拆解,直到它不是一個Promise對象,取其中的值。

那就讓我們來看看這個resolvePromise到底長啥樣。

function resolvePromise(promise2,x,resolve,reject){ //判斷x和promise2之間的關(guān)系
//因為promise2是上一個promise.then后的返回結(jié)果,所以如果相同,會導(dǎo)致下面的.then會是同一個promise2,一直都是,沒有盡頭
if(x === promise2){//相當(dāng)于promise.then之后return了自己,因為then會等待return后的promise,導(dǎo)致自己等待自己,一直處于等待

return reject(new TypeError("循環(huán)引用"))

} //如果x不是null,是對象或者方法
if(x !== null && (typeof x === "object" || typeof x === "function")){ //為了判斷resolve過的就不用再reject了,(比如有reject和resolve的時候)

let called    try{//防止then出現(xiàn)異常,Object.defineProperty
  let then = x.then//取x的then方法可能會取到{then:{}},并沒有執(zhí)行
  if(typeof then === "function"){        //我們就認為他是promise,call他,因為then方法中的this來自自己的promise對象
    then.call(x,y=>{//第一個參數(shù)是將x這個promise方法作為this指向,后兩個參數(shù)分別為成功失敗回調(diào)
      if(called) return;
      called = true
      //因為可能promise中還有promise,所以需要遞歸
      resolvePromise(promise2,y,resolve,reject)
    },err=>{          if(called) return;
      called = true
      //一次錯誤就直接返回
      reject(err)
    })
  }else{        //如果是個普通對象就直接返回resolve作為結(jié)果
    resolve(x)
  }
}catch(e){      if(called) return;
  called = true
  reject(e)
}

}else{ //這里返回的是非函數(shù),非對象的值,就直接放在promise2的resolve中作為結(jié)果

resolve(x)

}
}
它的作用是用來將onFufilled的返回值進行判斷取值處理,把最后獲得的值放入最外面那層的Promise的resolve函數(shù)中。

參數(shù)promise2(then函數(shù)返回的Promise對象),x(onFufilled函數(shù)的返回值),resolve、reject(最外層的Promise上的resolve和reject)。

為什么要在一開始判斷promise2和x?:首先在Promise/A+中寫了需要判斷這兩者如果相等,需要拋出異常,我就來解釋一下為什么,如果這兩者相等,我們可以看下下面的例子,第一次p2是p1.then出來的結(jié)果是個Promise對象,這個Promise對象在被創(chuàng)建的時候調(diào)用了resolvePromise(promise2,x,resolve,reject)函數(shù),又因為x等于其本身,是個Promise,就需要then方法遞歸它,直到他不是Promise對象,但是x(p2)的結(jié)果還在等待,他卻想執(zhí)行自己的then方法,就會導(dǎo)致等待。

let p1 = new Promise((resolve,reject)=>{
resolve()
})let p2 = p1.then(d=>{ return p2
})
called是用來干嘛的?:called變量主要是用來判斷如果resolvePromise函數(shù)已經(jīng)resolve或者reject了,那就不需要在執(zhí)行下面的resolce或者reject。

為什么取then這個屬性?:因為我們需要去判斷x是否為Promise,then屬性如果為普通值,就直接resolve掉,如果是個function,就是Promise對象,之后我們就需要將這個x的then方法進行執(zhí)行,用call的原因是因為then方法里面this指向的問題。

為什么要遞歸去調(diào)用resolvePromise函數(shù)?:相信細心的人已經(jīng)發(fā)現(xiàn)了,我這里使用了遞歸調(diào)用法,首先這是Promise/A+中要求的,其次是業(yè)務(wù)場景的需求,當(dāng)我們碰到那種Promise的resolve里的Promise的resolve里又包了一個Promise的話,就需要遞歸取值,直到x不是Promise對象。

4.完善Promise
我們現(xiàn)在已經(jīng)基本完成了Promise的then方法,那么現(xiàn)在我們需要看看他的其他方法。

4.1 catch
相信大家都知道catch這個方法是用來捕獲Promise中的reject的值,也就是相當(dāng)于then方法中的onRejected回調(diào)函數(shù),那么問題就解決了。我們來看代碼。

//catch方法catch(onRejected){ return this.then(null,onRejected)
}
該方法是掛在Promise原型上的方法。當(dāng)我們調(diào)用catch傳callback的時候,就相當(dāng)于是調(diào)用了then方法。

4.2 resolve/reject
大家一定都看到過Promise.resolve()、Promise.reject()這兩種用法,它們的作用其實就是返回一個Promise對象,我們來實現(xiàn)一下。

//resolve方法Promise.resolve = function(val){ return new Promise((resolve,reject)=>{

resolve(val)

})
}//reject方法Promise.reject = function(val){ return new Promise((resolve,reject)=>{

reject(val)

})
}
這兩個方法是直接可以通過class調(diào)用的,原理就是返回一個內(nèi)部是resolve或reject的Promise對象。

4.3 all
all方法可以說是Promise中很常用的方法了,它的作用就是將一個數(shù)組的Promise對象放在其中,當(dāng)全部resolve的時候就會執(zhí)行then方法,當(dāng)有一個reject的時候就會執(zhí)行catch,并且他們的結(jié)果也是按著數(shù)組中的順序來排放的,那么我們來實現(xiàn)一下。

//all方法(獲取所有的promise,都執(zhí)行then,把結(jié)果放到數(shù)組,一起返回)Promise.all = function(promises){ let arr = [] let i = 0
function processData(index,data){

arr[index] = data
i++    if(i == promises.length){
  resolve(arr)
}

} return new Promise((resolve,reject)=>{ for(let i=0;i

  promises[i].then(data=>{
    processData(i,data)
  },reject)
}

})
}
其原理就是將參數(shù)中的數(shù)組取出遍歷,每當(dāng)執(zhí)行成功都會執(zhí)行processData方法,processData方法就是用來記錄每個Promise的值和它對應(yīng)的下標,當(dāng)執(zhí)行的次數(shù)等于數(shù)組長度時就會執(zhí)行resolve,把arr的值給then。這里會有一個坑,如果你是通過arr數(shù)組的長度來判斷他是否應(yīng)該resolve的話就會出錯,為什么呢?因為js數(shù)組的特性,導(dǎo)致如果先出來的是1位置上的值進arr,那么0位置上也會多一個空的值,所以不合理。

4.4 race
race方法雖然不常用,但是在Promise方法中也是一個能用得上的方法,它的作用是將一個Promise數(shù)組放入race中,哪個先執(zhí)行完,race就直接執(zhí)行完,并從then中取值。我們來實現(xiàn)一下吧。

//race方法Promise.race = function(promises){ return new Promise((resolve,reject)=>{ for(let i=0;i

  promises[i].then(resolve,reject)
}

})
}
原理大家應(yīng)該看懂了,很簡單,就是遍歷數(shù)組執(zhí)行Promise,如果有一個Promise執(zhí)行成功就resolve。

Promise語法糖 deferred
語法糖這三個字大家一定很熟悉,作為一個很Swag的前端工程師,對async/await這對兄弟肯定很熟悉,沒錯他們就是generator的語法糖。而我們這里要講的語法糖是Promise的。

//promise語法糖 也用來測試Promise.deferred = Promise.defer = function(){ let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{

dfd.resolve = resolve
dfd.reject = reject

}) return dfd
}
什么作用呢?看下面代碼你就知道了

let fs = require("fs")let Promise = require("./promises")//Promise上的語法糖,為了防止嵌套,方便調(diào)用//壞處 錯誤處理不方便function read(){ let defer = Promise.defer()
fs.readFile("./1.txt","utf8",(err,data)=>{ if(err)defer.reject(err)

defer.resolve(data)

}) return defer.Promise
}
沒錯,我們可以方便的去調(diào)用他語法糖defer中的Promise對象。那么它還有沒有另外的方法呢?答案是有的。我們需要在全局上安裝promises-aplus-tests插件npm i promises-aplus-tests -g,再輸入promises-aplus-tests [js文件名] 即可驗證你的Promise的規(guī)范。

5.結(jié)尾
今天我們就做了一個屬于自己的Promise項目,理解里面的源碼,方法原理,希望大家都有收獲。當(dāng)然有什么意見大家都可以在評論區(qū)評論,peace and love。

git地址:https://github.com/Shinemax1/...

更多網(wǎng)易技術(shù)、產(chǎn)品、運營經(jīng)驗分享請訪問網(wǎng)易云社區(qū)。

文章來源: 網(wǎng)易云社區(qū)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/25325.html

相關(guān)文章

  • 【面試篇】寒冬求職季之你必須要懂的原生JS(中)

    摘要:如果你還沒讀過上篇上篇和中篇并無依賴關(guān)系,您可以讀過本文之后再閱讀上篇,可戳面試篇寒冬求職季之你必須要懂的原生上小姐姐花了近百個小時才完成這篇文章,篇幅較長,希望大家閱讀時多花點耐心,力求真正的掌握相關(guān)知識點。 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原型鏈,就...

    Mike617 評論0 收藏0
  • 【面試篇】寒冬求職季之你必須要懂的原生JS(上)

    摘要:只能遍歷數(shù)組,不能中斷,返回值是修改后的數(shù)組。這在語法上,稱為暫時性死區(qū)。作用域鏈無論是還是查詢,都會在當(dāng)前的作用域開始查找,如果沒有找到,就會向上級作用域繼續(xù)查找目標標識符,每次上升一個作用域,一直到全局作用域為止。 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原...

    寵來也 評論0 收藏0
  • 【面試篇】寒冬求職季之你必須要懂的原生JS(上)

    摘要:循環(huán)可以使用的范圍包括數(shù)組和結(jié)構(gòu)某些類似數(shù)組的對象對象,以及字符串。只能遍歷數(shù)組,不能中斷,返回值是修改后的數(shù)組。除了之外,等,也有同樣的問題。聲明一個只讀的常量。這在語法上,稱為暫時性死區(qū)。暫時性死區(qū)也意味著不再是一個百分百安全的操作。 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包...

    AlphaWatch 評論0 收藏0
  • 【面試篇】寒冬求職季之你必須要懂的原生JS(中)

    摘要:如果你還沒讀過上篇上篇和中篇并無依賴關(guān)系,您可以讀過本文之后再閱讀上篇,可戳面試篇寒冬求職季之你必須要懂的原生上小姐姐花了近百個小時才完成這篇文章,篇幅較長,希望大家閱讀時多花點耐心,力求真正的掌握相關(guān)知識點。 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原型鏈,就能獲得...

    andycall 評論0 收藏0
  • 前端最強面經(jīng)匯總

    摘要:獲取的對象范圍方法獲取的是最終應(yīng)用在元素上的所有屬性對象即使沒有代碼,也會把默認的祖宗八代都顯示出來而只能獲取元素屬性中的樣式。因此對于一個光禿禿的元素,方法返回對象中屬性值如果有就是據(jù)我測試不同環(huán)境結(jié)果可能有差異而就是。 花了很長時間整理的前端面試資源,喜歡請大家不要吝嗇star~ 別只收藏,點個贊,點個star再走哈~ 持續(xù)更新中……,可以關(guān)注下github 項目地址 https:...

    wangjuntytl 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<