從0最先構建本身的前端學問系統-JS-隨着範例學Promise

媒介

Promise作為ES6極為重要的一個特徵,將我們從無窮的回調地獄中擺脫出來,變成鏈式的編寫回調,大大提高的代碼的可讀性。

運用Promise是極為簡樸的,但只停留在會運用階段照樣會讓我們不知不覺踩到一些坑的。本文會連繫Promise/A+範例與示例來深切進修Promise

本文較長,例子許多,末端有一些運用 ^_^

Promise

  • 兼容性

    Promise作為是瀏覽器的內置函數對象,除IE不支撐外一切主流版本的瀏覽器都是支撐的,如不需支撐IE能夠在不引入polyfill的情況下寧神食用

  • 作用

    js是單線程的,異步是經由過程Event Loop來完成的,那末須要一種越發友愛的體式格局來完成我們的異步操縱,而不是無窮的回調嵌套

    // no promise
    callback(() => {
      callback(() => {
        callback(() => {
          ...
        })
      })
    })
    
    // with promise
    new Promise((resolve, reject) => {
    
    }).then(() => {
    
    }).then(() => {
    
    }).then(() => {
    
    })
  • API

    先來簡樸看一下Promise供應的一些API(注重辨別靜態要領與實例要領)

    • 組織函數Promise

      Promise是一個組織函數,能夠經由過程new操縱符來建立一個Promise實例

      let promise = new Promise((resolve, reject) => {
        resolve(1)
      })
    • 靜態要領

      • Promise.resolve
      • Promise.reject
      • Promise.all
      • Promise.race
    • 實例要領

      • Promise.prototype.then
      • Promise.prototype.catch
  • 連繫範例與樣例

    • 範例

      官方英文原版範例

      tips:

      1. 範例是範例,完成是完成,完成是根據範例來完成的,但不肯定完整等同於範例。
      2. 本文僅限瀏覽器環境測試,node環境可能會不一致
    • 狀況

      一個Promise實例只能處於pending,fulfilled,rejected三種狀況中的一種。每次建立的promise實例都邑處於pending狀況,而且只能由pending變成fulfilledreject狀況。一旦處於fulfilledrejected狀況不管舉行什麼操縱都不會再次變動狀況(範例2.1)

      // 建立一個處於pending狀況的promise實例
      let promise1 = new Promise((resolve, reject) => {
      
      })
      
      // 挪用resolve()會使promise從pending狀況變成fulfilled狀況
      let promise2 = new Promise((resolve, reject) => {
        resolve(1)
      })
      
      // 挪用reject()會使promise從pending狀況變成rejected狀況
      let promise3 = new Promise((resolve, reject) => {
        reject(1)
      })
      
      // 不管如何變動resolve與reject的實行遞次,promise4一直只會處於先挪用後轉換的狀況(狀況一旦變成fulfilled或rejected后不會再次轉變)
      let promise4 = new Promise((resolve, reject) => {
        resolve(1)
        reject(1)
      })
    • then

      • 參數

        實例要領then的回調函數接收兩個參數,onFulfilledonRejected,都為可選參數(範例2.2.1)

        // onFulfilled會在狀況變成fulfilled后挪用,onFulfilled參數value為resolve的第一個參數,onRejected同理。(範例2.2.2與2.2.3)
        
        let promise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(1)
          }, 1000) 
        })
        
        promise.then((value) => {
          console.log(value)  // 1秒后輸出1
        }, (reason) => {
        
        })
        
        // 可被統一個promise屢次挪用(範例2.2.6)
        promise.then()
        
        promise.then((value) => {
          console.log(value) // 1秒后輸出1
        })
      • 返回值

        then要領必需返回一個promise實例(範例2.2.7)

        假定 promise2 = promise1.then(onFulfilled, onRejected)

        var a = new Promise((resolve, reject) => {
          resolve(1)
        })
        
        var b = new Promise((resolve, reject) => {
          reject(1)
        })
        
        // 假如```onFulfilled```或```onRejected```返回了一個value ```x```,運轉promise處置懲罰順序(下一節), [[Resolve]](promise2, x) (範例2.2.7.1)
        var a1 = a.then(function onFulfilled(value) {
          return 1
        }, function onRejected (reason) {
        
        })
        
        var b1 = b.then(function onFulfilled(value) {
          
        }, function onRejected (reason) {
          return 1
        })
        
        // 假如```onFulfilled```或```onRejected```拋出了一個exception ```e```, ```promise2```必需以e作為reason rejected (範例2.2.7.2)
        // 故下方a2 b2 都為狀況是rejected的promise實例
        var a2 = a.then(function onFulfilled(value) {
          throw Error('test')
        }, function onRejected (reason) {
        
        })
        
        var b2 = b.then(function onFulfilled(value) {
          
        }, function onRejected (reason) {
          throw Error('test')
        })
        
        // 假如promise1處於fulfilled狀況而且onFulfilled不是一個函數,那末promise2會以與promise1具有雷同value和雷同的狀況, 然則與promise1並非統一Promise實例;若為rejected狀況以此類推
        var a3 = a.then()
        a3 === a // false
        var b3 = b.then()
        b3 === b // false
    • 處置懲罰順序resolve

      在範例中, promise處置懲罰順序是一個以promisevalue作為輸入的籠統操縱,我們示意為[[Resolve]](promise, x)

      能夠以為在完成里Promise.resolve()new Promise((resolve, reject) => {})中的resolve都為處置懲罰順序。範例中x對應完成中resolve的第一個參數,範例中promise在完成中等價於當前promise

      能夠明白為Promise.resolve(x)new Promise(resolve) => {resolve(x)}等價

      var c = new Promise((resolve, reject) => {
        resolve(1)
      })
      
      var d = new Promise((resolve, reject) => {
        reject(1)
      })
      
      var e = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('5s后resolve')
        }, 5000)
      })
      
      // 在返回值章節我們相識到,onFulfilled假如為函數在不拋出毛病的情況下,會挪用處置懲罰順序處置懲罰返回值x
      // 假如x是promise, 採用他的狀況(注重,但then的返回值並不與x相稱)(範例2.3.2)
      var c1 = Promise.resolve().then(function onFulfilled() {
        return c
      })
      
      c1 === c // false
      
      // pending狀況下的,只需e狀況變成,e1也會轉變(範例2.3.2.1) 
      var e1 = Promise.resolve().then(function onFulfilled () {
        return e
      })
      
      var c2 = Promise.resolve()
      // 假如promise和x是雷同的對象, 運用TypeError作為reason reject promise(範例2.3.1)
      var c3 = c2.then(function onFulfilled () {
        return c3
      })

      觀點:假如一個函數或許對象具有then屬性,那末叫做thenable(例: { then: function(){} })

      // 假如x是thenable 但x.then不是一個函數或許對象,直接返回狀況為fulfilled,value為x的promise實例(範例2.3.3.4)
      var c4 = c1.then(function onFulfilled () {
        return {}
      })
      
      var c5 = c1.then(function onFulfilled () {
        return {
          then: 2
        }
      })
      
      // 假如x是thenable 而且x.then是一個函數,那末就會挪用這個then要領實行,而且兩個參數resolve與reject具有轉變返回的promise狀況的權益,會按處置懲罰順序處置懲罰,假如不挪用兩個參數要領,則返回的promise為pending狀況,值得一提的是,挪用then要領的時刻回以返回對象x作為then的this綁定挪用(範例 2.3.3.3)
      
      var c6 = c1.then(function onFulfilled () {
        return {
          test: 'test',
          then: function (resolve, reject) {
            console.log(this.test)
            resolve('thenable')
          }
        }
      })
      
      var c7 = c1.then(function onFulfilled () {
        function x () {}
        x.test = 'test'
        x.then = function (resolve, reject) {
          console.log(this.test)
          resolve('thenable')
        }
        return x
      })
      
      var c8 = c1.then(function onFulfilled () {
        return {
          test: 'test',
          then: function (resolve, reject) {
            console.log(this.test)
            reject('thenable')
          }
        }
      })
      
      // 返回pending狀況的promise
      var c9 = c1.then(function onFulfilled () {
        return {
          then: function () {}
        }
      })
      
      // 假如x不是對象也不是函數,則返回狀況為fulfilled的promise,value為x
      
      var c10 = c1.then(function onFulfilled () {
        return 'hehe'
      })

綜上能夠總結幾點

1.  ```new Promise, promise.then, Promise.resolve()```都邑返回promise實例
2.  Promise狀況一旦轉變不可再變動
3.  ```then```要領返回對象的差別會致使差別的效果,假如不測返回了thenable有可能會形成意想不到的效果

運用

  • 封裝一個異步要求

    var promiseXHR = new Promise((resolve, reject) => {
      var xhr = new XMLHttpRequest()
      xhr.open('GET', 'http://www.baidu.com', true)
      xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
          resolve(xhr.response)
        } else {
          reject()
        }
      }
      xhr.send(data)
    })
  • 同時要求順次處置懲罰

    // 假設有5個章節的數據,我們須要離別獵取並讀取到c中,應用promise和es6數組api我們能夠寫出簡約文雅的代碼完成這個需求
    
    var list = ['Chapter1', 'Chapter2', 'Chapter3', 'Chapter4', 'Chapter5']
    
    var getData = function (key) {
      return new Promise((resolve, reject) => {
        // 模仿一些返回時候不雷同的異步操縱
        setTimeout(() => {
          resolve(key)
        }, 4000 * Math.random())
      })
    }
    
    var c = ''
    
    list.map(i => getData(i))
        .reduce((accumulator, current) => {
          console.log(accumulator)
          return accumulator.then(() => {
            return current
          }).then(value => {
            c+= value
          })
        }, Promise.resolve(''))
  • catch

    明顯在我們一樣平常事情中常用到的catch要領,為何到如今還一點都沒有提到呢?

    由於catch要領就是then要領封裝出來的語法糖罷了,由於假如只想捕捉毛病,還每次都要謄寫then的第一個參數,真的是貧苦至極,如今我們來寫一個本身的catch

    Promise.prototype.mycatch = function (cb) {
        return this.then(1, function onRejected (reason) {
          return cb(reason)
        })
      }

    // 用到了範例中假如onFulfilled不是一個函數,則疏忽,並運用本來promise狀況與value

  • finally

    有的瀏覽器完成了finally,而有的並沒有,從需求上來說finally只不過是終究要實行一個函數,我們須要把應當有的狀況或許非常都繼承通報,不受其影響。實行的函數與promise的value無任何關聯

    Promise.prototype.myfinally = function (cb) {
      return this.then(function onFulfilled (value) {
        return Promise.resolve(cb()).then(() => value)
      }, function onRejected (reason) {
        return Promise.resolve(cb()).then(() => {
          throw reason
        })
      })
    }

跋文

經由過程瀏覽範例並寫demo舉行考證測試,對Promise的明白越發深切了,也能更好的運用它了
假如喜好能夠star一下,會不斷更新github地點

    原文作者:kedaya
    原文地址: https://segmentfault.com/a/1190000014464934
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞