成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript五十問——淺入深出,自己實現(xiàn)一個 ES 6 Promise

hiyayiji / 3064人閱讀

摘要:以上實現(xiàn)了最簡單的一個測試代碼當然,這不能算是一個,目前僅僅實現(xiàn)了根據(jù)狀態(tài)調(diào)用不同的回調(diào)函數(shù)。靜態(tài)函數(shù)接下來是的各種靜態(tài)函數(shù)每一個都執(zhí)行完畢后返回總結(jié)現(xiàn)在,一個完整的對象就完成了。

前言

說到 ES6,Promise 是繞不過的問題;如果說 ES6 的 Class 是基于 Javascript 原型繼承的封裝,那么 Promise 則是對 callback 回調(diào)機制的改進。這篇文章,不談 Promise 的實際應用;聊一下 Promise 的實現(xiàn)原理,從最簡單的解決方案入手,一步一步的自己實現(xiàn)一個 SimplePromise。

正文 入門

從最簡單的 Promise 初始化和使用入手:

const pro = new Promise ((res, rej) => {})

pro.then(data => {}, err => {})

Promise 的構(gòu)造函數(shù)如上,需要傳遞一個函數(shù)作為參數(shù),這個函數(shù)有兩個變量: resolve, reject。而 Promise 有不同的執(zhí)行狀態(tài),分三種情況:Resolve, Reject, Pending。根據(jù)以上的信息,寫出最基本的 SimplePromise 的類結(jié)構(gòu):

class SimplePromise{
  constructor(handler){
    this._status = "PENDING"
    handler(this._resolve.bind(this), this._reject.bind(this))//參數(shù)函數(shù)的作用域指向Class
  }

  _resolve(){}
  _reject(){}
}

接下來思考一下_resolve_reject兩個函數(shù)的作用。我們知道,Promise 根據(jù) then 方法來執(zhí)行回調(diào),而 then 是根據(jù)狀態(tài)判斷要執(zhí)行的回調(diào)函數(shù)。不難推導出,_resolve_reject正是根據(jù)handler的執(zhí)行來進行狀態(tài)變更的,而狀態(tài)只能由PendingResloveRejected轉(zhuǎn)換,所以有:

class SimplePromise{
  constructor(handler){
   ...
  }

  _resolve(val){//異步返回的數(shù)據(jù)
    if(this._status === "PENDING"){//保證狀態(tài)的不可逆性
      this._status = "RESOLVED"
      this._value = val
    }
  }
  _reject(val){
    if(this._status === "PENDING"){
      this._status = "REJECTED"
      this._value = val
    }
  }
}
then的調(diào)用邏輯

下面分析 then 函數(shù)的邏輯,從調(diào)用入手:

pro.then(data => {}, err => {})

then 接收兩個參數(shù),第一個是執(zhí)行成功調(diào)用的函數(shù),第二個是執(zhí)行失敗調(diào)用的函數(shù)。

class SimplePromise{
  constructor(handler){
    ...
  }

  _resolve(val){
   ...
  }
  _reject(val){
    ...
  }
  then(success, fail){
    switch (this._status){
      case "PENDING":
        break;
      case "RESOLVED":
        success(this._value)
        break;

      case "REJECTED":
        fail(this._value)
        break;
    }
  }
}

以上實現(xiàn)了最簡單的一個 Promise
測試代碼:

const pro = new SimplePromise(function(res, rej) {
    let random = Math.random() * 10
    if(random > 5){
      res("success")
    }
    else{
      rej("fail")
    }
})

pro.then(function(data) {
  console.log(data)
}, function(err) {
  console.log(err)
})

當然,這不能算是一個 Promise,目前僅僅實現(xiàn)了根據(jù)狀態(tài)調(diào)用不同的回調(diào)函數(shù)。還沒有實現(xiàn)異步。
那如何實現(xiàn)異步呢?關(guān)鍵在于 then 函數(shù),當判斷_statusPENDING時,如何延后調(diào)用 successfail函數(shù),等待狀態(tài)改變后再調(diào)用?

支持異步

這里采用數(shù)組來存儲 fail 與 success 函數(shù):

class SimplePromise{
  constructor(handler){
    this.status = "PENDING"
    this._onSuccess = []//存儲fail 與 success 函數(shù)
    this._onFail = []
    handler(this._resolve.bind(this), this._reject.bind(this))
  }

  _resolve(val){
    if(this.status === "PENDING"){
      ...
      let temp
      while(this._onSuccess.length > 0){//依次執(zhí)行onSuccess中的回調(diào)函數(shù)
        temp = this._onSuccess.shift()
        temp(val)
      }
    }
  }
  _reject(val){
    if(this.status === "PENDING"){
     ...
      let temp
      while(this._onFail.length > 0){
        temp = this._onFail.shift()
        temp(val)
      }
    }
  }

  then (success, fail){
    switch (this.status){
      case "PENDING":
        this._onSuccess.push(success)
        this._onFail.push(fail)
        break;
      ...
    }
  }
}

使用 onSuccess onFail 來存儲回調(diào)函數(shù),當處理狀態(tài)為 PENDING 時,將回調(diào)函數(shù) push 到相應的數(shù)組里,當狀態(tài)變更后,依次執(zhí)行數(shù)組里的回調(diào)函數(shù)。

測試代碼:

const pro = new SimplePromise(function(res, rej) {
  setTimeout(function(){
    let random = Math.random() * 10
    if(random > 5){
      res("success")
    }
    else{
      rej("fail")
    }
  }, 2000)

})

pro.then(function(data) {
  console.log(data)
}, function(err) {
  console.log(err)
})

兩秒后,會執(zhí)行相應的回調(diào)。

到目前為止,最最最簡單的一個 Promise 骨架已經(jīng)基本完成了。但是還有很多功能待完成。現(xiàn)在可以稍微休息一下,喝個咖啡打個雞血,回來我們會繼續(xù)讓這個 Promise 骨架更加豐滿起來。

. . . . . .

完善Promise

歡迎回來,下面我們繼續(xù)完善我們的 Promise。
上面完成了一個最基礎(chǔ)的 Promise,然而還遠遠不夠。首先,Promise 需要實現(xiàn)鏈式調(diào)用,其次 Promise 還需要實現(xiàn) all race resolve reject 等靜態(tài)函數(shù)。

首先,如何實現(xiàn) then 的鏈式調(diào)用呢?需要 then 返回的也是一個 Promise。
于是有

class SimplePromise{
  ...
  then(success, fail){
      return new SimplePromise((nextSuccess, nextFail) => {
        const onFullfil = function(val){
          const res = success(val)
          nextSuccess(res)
        }

        const onReject = function(val){
          const res = fail(val)
          nextSuccess(res) ;
        }
        switch (this._status){
          case "PENDING":
            this._onSuccess.push(onFullfil)
            this._onFail.push(onReject)
            break;
          case "RESOLVED":
            onFullfil(this._value)
            break;

          case "REJECTED":
            onReject(this._value)
            break;
        }
      })
  }
}

測試代碼:

const sp = new SimplePromise(function (res, rej){
  setTimeout(function(){
    let random = Math.random() * 10
    random > 5 ? res(random) : rej(random)
  }, 1000)
})

sp.then(data => {
  console.log("more than 5 " + data)
  return data
}, err =>{
  console.log("less than 5 " + err)
  return err
}).then((data) => {
  console.log(data)

})
then的參數(shù)限制

完成了鏈式調(diào)用,then 方法還有許多其他限制:
下面思考 以下問題:代碼中四個使用 promise 的語句之間的不同點在哪兒?
假設(shè) doSomething 也 doSomethingElse 都返回 Promise

doSomething().then(function () {
    return doSomethingElse();
}).then(finalHandler);

doSomething().then(function () {
    doSomethingElse();
}).then(finalHandler);;

doSomething().then(doSomethingElse()).then(finalHandler);;

doSomething().then(doSomethingElse).then(finalHandler);;

答案 一會兒再揭曉,我們先來梳理以下then 方法對傳入不同類型參數(shù)的處理機制:
直接上代碼:

class SimplePromise{
  ...
  then(success, fail){
    return new SimplePromise((nextSuccess, nextFail) => {
      const onFullfil = function(val){
        if(typeof success !== "function"){
          nextSuccess(val)
        }
        else{
          const res = success(val)//success 的返回值
          if(res instanceof SimplePromise){//如果success 返回一個promise 對象
            res.then(nextSuccess, nextFail)
          }
          else{
            nextSuccess(res)
          }
        }
      }
      if(fail){
        const onReject = function(val){
        if(typeof fail !== "function"){
          nextSuccess(val)
        }
        else{
          const res = fail(val)
          if(res instanceof SimplePromise){
            res.then(nextSuccess, nextFail)
          }
          else{
            nextSuccess(res)
          }
        }
      }
      }
      else{
        onReject = function(){}
      }
      switch (this._status){
      case "PENDING":
        this._onSuccess.push(onFullfil)
        this._onFail.push(onReject)
        break;
      case "RESOLVED":
        onFullfil(this._value)
        break;

      case "REJECTED":
        onReject(this._value)
        break;
      }
    })
  }
}

對于傳入 then 方法的參數(shù),首先判斷其是否為 function,判斷為否,直接執(zhí)行 下一個 then 的 success 函數(shù);判斷為是,接著判斷函數(shù)的返回值 res 類型是否為 Promise,如果為否,直接執(zhí)行下一個 then 的 success 函數(shù),如果為是,通過 then 調(diào)用接下來的函數(shù)。
所以,上面的問題就不難得到答案了。



doSomething().then(function () {
    return doSomethingElse();//返回值為Promise
}).then(finalHandler);

RETURN:
doSomething
          --->doSomethingElse(undefined)
                                        ---> final(doSomethingElseResult)

doSomething().then(function () {
  doSomethingElse();//返回值為 undefined
}).then(finalHandler);

RETURN:
doSomething
          --->doSomethingElse(undefined)
          ---> final(undefined)


doSomething().then(doSomethingElse())//參數(shù) typeof != function
  .then(finalHandler);

RETURN:
doSomething
doSomethingElse(undefined)
            ---> final(doSomethingResult)

doSomething().then(doSomethingElse)//與1的調(diào)用方式是不同的
  .then(finalHandler);

RETURN:
doSomething
          --->doSomethingElse(doSomethingResult)
                                        ---> final(doSomethingElseResult)

好,then 方法已經(jīng)完善好了。

靜態(tài)函數(shù)

接下來是 Promise 的各種靜態(tài)函數(shù)

class SimplePromise(){
  ...
  static all(){}
  static race(){}
  static resolve(){}
  static reject(){}
}
all
 static all(promiselist){
      if(Array.isArray(promiselist)){
        const len = promiselist.length;
        const count = 0
        const arr = []
        return new SimplePromise((res, rej) => {
          for(let i = 0; i {
              arr[i] = data
              count ++
              if(count === len){//每一個Promise都執(zhí)行完畢后返回
                res(arr)
              }
            }, err => {
              rej(err)
            })
          }
        })
      }
    }
race
    static race(promiselist){
      if(Array.isArray(promiselist)){
        const len = promiselist.length
        return new SimplePromise((res, rej) =>{
          promiselist.forEach(item =>{
            this.resolve(item).then(data => {
              res(data)
            }, err =>{
              rej(err)
            })
          })
        })
      }
    }
resolve
    static resolve(obj){
      if(obj instanceof SimplePromise){
        return obj
      }
      else {
        return new SimplePromise((res) =>{
          res(obj)
        })
      }
    }
reject
    static reject(obj){
      if(obj instanceof SimplePromise){
        return obj
      }
      else {
        return new SimplePromise((res, rej) =>{
          rej(obj)
        })
      }
    }
總結(jié)

現(xiàn)在,一個完整的 Promise 對象就完成了?,F(xiàn)在來總結(jié)一下 callback 回調(diào)和 Promise 的異同吧。
其實,不管是 callback 還是 Promise,這二者都是將需要滯后執(zhí)行方法而提前聲明的方式,只不過 callback 的處理方式比較粗獷,將 cb 函數(shù)放到異步執(zhí)行的結(jié)尾;而 Promise 優(yōu)于 cb 的是通過定義了不同的執(zhí)行狀態(tài),更加細致的進行結(jié)果處理,提供了很好的 catch 機制,這是其一;其二,then 的鏈式調(diào)用解決了 cb 的回調(diào)地獄;但是 then 的鏈式調(diào)用也不是很好的解決方案,如果封裝不好,then里面套用大量的代碼的話也會引起代碼的不美觀和閱讀上的困難,這一方面的終極解決方法還是 es7 的 async/await。

后記

這篇文章的代碼是幾個星期以前寫的,參考的是思否上的一篇關(guān)于promise的文章;總結(jié)的是我對promise的理解和思考,如果有不準確或錯誤的地方還希望各位不吝賜教!希望大家積極反饋,一起交流!

參考文檔

談一談使用 Promise 的反模式

寫這篇文章的時候,我是參考我兩周前的代碼寫的,當時的代碼思路來源于思否上的謀篇博客,等我找到會貼上來

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100951.html

相關(guān)文章

  • 淺入深出setState(上篇)

    摘要:點燃引擎這是一個組件實現(xiàn)組件可交互所需的流程,輸出虛擬,虛擬轉(zhuǎn)為,再在上注冊事件,事件觸發(fā)修改數(shù)據(jù),在每次調(diào)用方法時,會自動執(zhí)行方法來更新虛擬,如果組件已經(jīng)被渲染,那么還會更新到中去。 Part one - setState點燃引擎 showImg(https://segmentfault.com/img/bVbdGkJ?w=849&h=270); 這是一個React組件實現(xiàn)組件可交互...

    wow_worktile 評論0 收藏0
  • Javascript 十問——實現(xiàn)的繼承多種方式

    摘要:組合繼承實現(xiàn)了屬性分離,方法共享下的完美繼承方案繼承我們的主角,,就是對組合繼承的改進。這也是為什么在子類構(gòu)造函數(shù)中一定要顯示調(diào)用的原因。 談到繼承,或者更廣義上的:一個對象可以使用另外一個對象的屬性或方法。實現(xiàn)起來無外乎有兩種方式:apply or call 改變this的作用域原型繼承 改變__proto__指向,添加作用域鏈 而JavaScript所有的繼承實現(xiàn),都是圍繞以上兩點...

    BlackHole1 評論0 收藏0
  • JavaScript 十問——從源碼分析 ES6 Class 的實現(xiàn)機制

    摘要:防止類的構(gòu)造函數(shù)以普通函數(shù)的方式調(diào)用。這個函數(shù)的主要作用是通過給類添加方法,其中將靜態(tài)方法添加到構(gòu)造函數(shù)上,將非靜態(tài)的方法添加到構(gòu)造函數(shù)的原型對象上。 Class是ES6中新加入的繼承機制,實際是Javascript關(guān)于原型繼承機制的語法糖,本質(zhì)上是對原型繼承的封裝。本文將會討論:1、ES6 class的實現(xiàn)細2、相關(guān)Object API盤點3、Javascript中的繼承實現(xiàn)方案盤點...

    LeexMuller 評論0 收藏0
  • Javascript十問——從源頭細說Webpack與Gulp

    摘要:前言與是目前圈子內(nèi)比較活躍的前端構(gòu)建工具。對于初學者來說,對這二者往往容易認識不清,今天,就從事件的源頭,說清楚與。它可以將許多松散的模塊按照依賴和規(guī)則打包成符合生產(chǎn)環(huán)境部署的前端資源。打包后形成的文件出口。 前言:Webpack 與 gulp是目前圈子內(nèi)比較活躍的前端構(gòu)建工具。網(wǎng)上有很多二者比較的文章,面試中也會經(jīng)常遇到gulp,Webpack的區(qū)別這樣的問題。對于初學者來說,對這二...

    lwx12525 評論0 收藏0

發(fā)表評論

0條評論

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