成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

ES6 Promise 全面總結

Towers / 2834人閱讀

摘要:它們其實是在運行完成后,主動向該回調函數(shù)中傳入的參數(shù)。該方法其實是的別名,用于指定狀態(tài)轉為失敗時的回調函數(shù)。構造器回調函數(shù)參數(shù)中的和用于更改當前的狀態(tài),并將其值返回給當前的方法的參數(shù)。

ES6 Promise對象

ES6中,新增了Promise對象,它主要用于處理異步回調代碼,讓代碼不至于陷入回調嵌套的死路中。

@-v-@

1. Promise本質

Promise本質上是一個 函數(shù) ,更確切地說,它是一個 構造器 ,專門用來構造對象的。
它接受一個函數(shù)作為參數(shù),并返回一個對象,大致情況如下:

  function Promise( fn ){
    // var this = {}
    // Object.setPrototypeOf(this, Promise.prototype)
    // 接下來,是Promise函數(shù)具體要實現(xiàn)的功能,這部分由系統(tǒng)幫我們完成
    ...  
    // 最后返回這個Promise實例
    return this
  }

Promise函數(shù)的參數(shù),作為函數(shù)形式存在,需要我們手動去編寫。
它需要兩個參數(shù),情況如下:

  function fn(resolve, reject){
    ...  // 我們自己寫的邏輯代碼
  }

Promise函數(shù)的返回值是一個對象,準確來說,是Promise自己生成的實例。
其實Promise函數(shù)的使命,就是構建出它的實例,并且負責幫我們管理這些實例。
該實例有三種狀態(tài),分別是: 進行 狀態(tài)、 完成 狀態(tài) 和 失敗 狀態(tài)。
該實例只能從“ 進行 狀態(tài)”轉變?yōu)椤?完成 狀態(tài)”,或從“ 進行 狀態(tài)”轉變?yōu)椤?失敗 狀態(tài)”,這個過程不可逆轉,也不可能存在其他可能。因為Promise就是用來管理業(yè)務狀態(tài)的一種機制,它能夠保證業(yè)務的順序執(zhí)行,而不出現(xiàn)混亂。

這就好比我們在家里炒一份菜,是只可能存在“ 正在炒菜 ”、“ 炒好了 ”和“ 炒糊了 ”這三個階段的,而“正在炒菜”的狀態(tài)肯定是會優(yōu)先存在于“炒好了”和“炒糊了”兩個狀態(tài)前面,“炒好了”和“炒糊了”本身又是兩個 互斥的事件 ,所以這個過程,只可能出現(xiàn)從“正在炒菜”狀態(tài)過渡到“炒好了”或者“炒糊了”狀態(tài)的情況,永遠不可能從“炒好了”過渡到“炒糊了”狀態(tài),也不可能從“炒糊了”過渡到“炒好了”狀態(tài)。

那么,這些由Promise函數(shù)構建出來的對象,究竟有著什么用處呢?
我們先來看一組代碼:

  fn( ( ( ( ()=>{} )=>{} )=>{} )=>{} )

像這樣回調之中調回調的情況,在Node開發(fā)中,是一件很常見的事。
Node本身是一個無阻塞、無空耗、并發(fā)、依賴于系統(tǒng)底層讀寫事件的運行環(huán)境,它的回調機制保證了它在異步并發(fā)執(zhí)行過程中回調鏈的獨立性和抗干擾能力,但同時也帶來了很大的副作用,最大的麻煩就是,采用普通回調方式書寫出來的Node回調代碼十分混亂。

其實,面向過程或面向對象的函數(shù)式編程,本身就是一個巨大的“函數(shù)調用”過程。我們在代碼中使用函數(shù),并在函數(shù)中調用函數(shù),運行環(huán)境幫助我們維護一個或多個函數(shù)棧,以實現(xiàn)程序的有序執(zhí)行,及增強軟件后期維護的便利性。

但如果我們能把這種不斷調用的過程給攤開成 平面 ,而不要使函數(shù)相互嵌套,就會使我們的軟件可維護性提升很大一個臺階。我們只需要將原本寫好的功能一個個羅列出來,并構造出一根供函數(shù)調用的鏈條,把這些功能一個個地按需調用,軟件的功能不就實現(xiàn)了么?而且還更清晰明了。
Promise幫助我們將函數(shù)攤開來,形成一根調用鏈條,讓程序有序執(zhí)行。

每一個返回值為Promise實例的函數(shù),都是 Promise調用鏈條上的一個結點 ,這個Promise實例維護著該處函數(shù)的運行狀態(tài),并決定著自身的生存周期。它的寫法大致是這樣的:

  // 執(zhí)行一個返回值為promise的函數(shù) 并通過resolve或reject返回
  promiseFn_1(...)
  // 將多個返回值為promise的函數(shù)合成一個 并通過resolve或reject返回
  // Promise.all( promiseFn_all_1, promiseFn_all_2, ... )
  // Promise.race( promiseFn_race_1, promiseFn_race_2, ... )
  //
  .then(
    (...resolveArgs)=>{ ... promiseFn_resolve_1(...) ... },
    (...rejectArgs)=>{ ... promiseFn_reject_1(...) ... },
  )
  .then(
    (...resolveArgs)=>{ ... promiseFn3_resolve_2(...) ... },
    (...rejectArgs)=>{ ... promiseFn3_reject_2(...) ... },
  )
  ...
  .catch(
    (...rejectArgs)=>{ ... promiseFn_catch_1(...) ... }
  )
  ...
  .finally(
    (...simpleArgs)=>{ ... }
  )

上面的代碼看似及其繁瑣,其實結構層次已經比使用普通回調方式書寫的代碼好很多了(雖然還是顯得有些混亂)。
當我們了解了Promise中這些函數(shù)(如then()、catch()、finally())的具體意思,就會明白它的具體意思了。
接下來我們就來構建一個Promise實例,看一看這根“鏈條”上的結點(也就是上面以“promiseFn_”開頭的函數(shù))到底長什么樣。

  function promiseFn_1(path, options){
    return new Promise((resolve,reject)=>{
      // 需要執(zhí)行的具體代碼,一般情況下,是調用一個帶有回調參數(shù)的函數(shù)
      // 此處使用fs模塊中的readFile函數(shù)作為示例
      fs.readFile(path, options, (err,data)=>{
        if(err){
          reject(err)
          // 這樣使用可能會更好:
          // throw new Error(path+" :  文件讀取出現(xiàn)未知的錯誤!")
        }
        resolve(data)
      })
    })
  }

上面Promise參數(shù)函數(shù)中,出現(xiàn)了兩個陌生的參數(shù),resolve和reject。它們其實是在Promise運行完成后,主動向該回調函數(shù)中傳入的參數(shù)。這個過程,由Promise函數(shù)自動幫我們完成。
resolve和reject都是與Promise實例相關的函數(shù),用于改變Promise實例的狀態(tài)。
resolve函數(shù)能使Promise實例從“進行”狀態(tài)變成“完成”狀態(tài),并將自己接受到的參數(shù)傳給下一個promise對象。
reject函數(shù)能使Promise實例從“進行”狀態(tài)變成“失敗”狀態(tài),并將自己接受到的參數(shù)傳給下一個promise對象(一般是一個錯誤對象)。

2. Promise的幾個重要方法 2.1 promise Promise.prototype.then( resolveFn, [rejectFn] )
  @param resolveFn( ...args )  
    函數(shù),當Promise實例狀態(tài)變?yōu)椤巴瓿伞睜顟B(tài)時會被執(zhí)行,  
    用于將從當前promise中取出reresolve( ...args )中得到的參數(shù)(...args),  
    并進行相應的操作,比如將(args)傳入另一個封裝了promise構造器的函數(shù),  
    并將該函數(shù)執(zhí)行完成后返回的promise實例返回  
    @param ...args  
      參數(shù)列表,當前promise實例處于“完成”狀態(tài)時,通過resolve(...args)得到的值。  
  @param [rejectFn( ...args )]  
    函數(shù),可選,當Promise實例狀態(tài)變?yōu)椤笆 睜顟B(tài)時會被執(zhí)行,  
    用于將從當前promise中取出reject( ...args )中得到的參數(shù)(...args),  
    并進行相應的操作,比如將(args)傳入另一個封裝了promise構造器的函數(shù),  
    并將該函數(shù)執(zhí)行完成后返回的promise實例返回  
    @param ...args  
      參數(shù)列表,當前promise處于“完成”狀態(tài)時,通過resolve(...args)得到的值。  
  @return promise  
    promise對象,resolveFn或rejectFn執(zhí)行后的返回值,  
    我們一般會在fn中調用另一個封裝了promise構造器的函數(shù),  
    然后將其返回給then()方法,then()方法再將其作為then的返回值返回給當前鏈式調用處,  
    如果fn()返回的不是一個promise對象,then()會幫我們將fn()返回值封裝成promise對象,  
    這樣,我們就可以確保能夠鏈式調用then()方法,并取得當前promise中獲得的函數(shù)運行結果。  

then()方法定義在Promise.prototype上,用于為Promise實例添加狀態(tài)更改時的回調函數(shù),相當于監(jiān)聽一樣。
當當前promise實例狀態(tài)變?yōu)椤巴瓿伞睜顟B(tài)時,resolveFn函數(shù)自動執(zhí)行。
當當前promise實例狀態(tài)變?yōu)椤笆 睜顟B(tài)時,rejectFn函數(shù)自動執(zhí)行。

2.2 promise Promise.prototype.catch( rejectFn )
  @param rejectFn( ...args )  
    函數(shù),當Promise實例狀態(tài)變?yōu)椤笆 睜顟B(tài)時會被執(zhí)行,  
    用于將從當前promise中取出reject( ...args )中得到的參數(shù)(...args),  
    并進行相應的操作,比如將(args)傳入另一個封裝了promise構造器的函數(shù),  
    并將該函數(shù)執(zhí)行完成后返回的promise實例返回  
    @param ...args  
      參數(shù)列表,當前promise處于“完成”狀態(tài)時,通過resolve(...args)得到的值。  
  @return promise  
    promise對象,rejectFn執(zhí)行后的返回值,  
    如果fn()返回的不是一個promise對象,catch()會幫我們將fn()返回值封裝成promise對象,  
    并將其返回,以確保promise能夠被繼續(xù)鏈式調用下去。  

該方法其實是“.then(null, rejectFn)”的別名,用于指定狀態(tài)轉為“失敗”時的回調函數(shù)。
建議不要在then()方法中定義第二個參數(shù),而應該使用catch(),結構層次會更好一些。
如果沒有使用catch()方法指定錯誤錯誤處理的回調函數(shù),promise實例拋出的錯誤不會傳遞到外層代碼。
如果promise狀態(tài)已經變?yōu)榱藃esolved(“失敗”狀態(tài)),再拋出任何錯誤,都是無效的。
promise實例中拋出的錯誤具有冒泡的特性,它會一直向后傳遞,直到被捕獲為止。

2.3 Promise.all( [promise1, promise2, ..., promisen] )
  @param [promise1, promise2, ..., promisen]
    可遍歷對象,一個由promise對象構成的可遍歷對象,常用數(shù)組表示
  @return promise
    promise對象

Promise.all()用于將多個Promise實例包裝成一個新的Promise實例,并返回。
Promise.all()方法接受一個由Promise實例組成的可遍歷對象。如果可遍歷對象中存在有不是Promise實例的元素,就會調用Promise.resolve()方法,將其轉為Promise實例。
本文的可遍歷對象,指的是那些具有Iterator接口的對象,如Array、WeakSet、Map、Set、WeakMap等函數(shù)的實例。
Promise.all()方法返回的Promise實例的狀態(tài)分成兩種情況:

可遍歷對象中的Promise實例狀態(tài)全變?yōu)?完成 狀態(tài)時,該實例的狀態(tài)才會轉變?yōu)?完成 狀態(tài),此時,可遍歷對象中的Promise實例的返回值會組成一個數(shù)組,傳給該實例的回調。

可遍歷對象只要存在Promise實例狀態(tài)轉為 失敗 狀態(tài)時,該實例的狀態(tài)就會轉變?yōu)?失敗 狀態(tài),此時,第一個轉為 失敗 狀態(tài)的Promise實例的返回值會傳給該實例的回調。

2.4 Promise.race( [promise1, promise2, ..., promisen] )
  @param [promise1, promise2, ..., promisen]
    可遍歷對象,一個由promise對象構成的可遍歷對象,常用數(shù)組表示
  @return promise
    promise對象

Promise.race()與Promise.all()用法基本上一致,功能上也幾乎相同,唯一的差異就是:
Promise.race()方法返回的Promise實例的狀態(tài)分成兩種情況:

可遍歷對象只要存在Promise實例狀態(tài)轉為 完成 狀態(tài)時,該實例的狀態(tài)才會轉變?yōu)?完成 狀態(tài),此時,第一個轉為 完成 狀態(tài)的Promise實例的返回值,會作為該實例的then()方法的回調函數(shù)的參數(shù)。

可遍歷對象只要存在Promise實例狀態(tài)轉為 失敗 狀態(tài)時,該實例的狀態(tài)就會轉變?yōu)?失敗 狀態(tài),此時,第一個轉為 失敗 狀態(tài)的Promise實例的返回值,會作為該實例的then()方法的回調函數(shù)的參數(shù)。

2.5 promise Promise.resolve( notHaveThenMethodObject )
  @param notHaveThenMethodObject
    對象,一個原型鏈上不具有then()方法的對象
  @return promise
    promise對象

如果Promise.resolve()的參數(shù)的原型鏈上不具有then方法,則返回一個新的Promise實例,且其狀態(tài)為 完成 狀態(tài),并且會將它的參數(shù)作為該實例的then()方法的回調函數(shù)的參數(shù)。
如果Promise.resolve()的參數(shù)是一個Promise實例(原型鏈上具有then方法),則將其原封不動地返回。
Promise.resolve()方法允許調用時不使用任何參數(shù)。

2.6 promise Promise.reject( something )
  @param something
    任意值,用于傳遞給返回值的then()方法的回調函數(shù)參數(shù)的值
  @return promise
    promise對象

Promise.reject方法的用法和resolve方法基本一樣,只是它返回的Promise實例,狀態(tài)都是 失敗 狀態(tài)。
Promise.reject方法的參數(shù)會被作為該實例的then()方法的回調函數(shù)的參數(shù)。
Promise.resolve()方法允許調用時不使用任何參數(shù)。

Promise構造器回調函數(shù)參數(shù)中的 resolvereject 和Promise構造器方法中的 reject()resolve() 效果是不一樣的。
Promise構造器回調函數(shù)參數(shù)中的 resolvereject 用于更改當前Promise的狀態(tài),并將其值返回給當前Promise的then()方法的參數(shù)。
Promise構造器方法中的 reject()resolve() 可以直接返回一個已經改變狀態(tài)的新的Promise對象。

Promise.reject() Promise.resolve()

new Promise((resolve, reject)=>{ resolve(...) 或 reject(...) })

2.7 Promise.prototype.done( [resolveFn], [rejectFn] )
  @param [resolveFn( ...args )]  
    函數(shù),可選,當Promise實例狀態(tài)變?yōu)椤巴瓿伞睜顟B(tài)時會被執(zhí)行,  
    用于將從當前promise中取出reresolve( ...args )中得到的參數(shù)(...args),  
    并進行相應的操作,比如將(args)傳入另一個封裝了promise構造器的函數(shù),  
    并將該函數(shù)執(zhí)行完成后返回的promise實例返回  
    @param ...args  
      參數(shù)列表,當前promise實例處于“完成”狀態(tài)時,通過resolve(...args)得到的值。  
  @param [rejectFn( ...args )]  
    函數(shù),可選,當Promise實例狀態(tài)變?yōu)椤笆 睜顟B(tài)時會被執(zhí)行,  
    用于將從當前promise中取出reject( ...args )中得到的參數(shù)(...args),  
    并進行相應的操作,比如將(args)傳入另一個封裝了promise構造器的函數(shù),  
    并將該函數(shù)執(zhí)行完成后返回的promise實例返回  
    @param ...args  
      參數(shù)列表,當前promise處于“完成”狀態(tài)時,通過resolve(...args)得到的值。  

不管以then()或catch()方法結尾,若最后一個方法拋出錯誤,則在內部可能無法捕捉到該錯誤,外界也無法獲得,為了避免這種情況發(fā)生,Promise構造器的原型鏈上提供了done()方法。
promise.done()方法總是處于會調鏈的低端,它可以捕捉到任何在回調鏈上拋出的錯誤,并將其拋出。

2.8 Promise.prototype.finally( simpleFn )
  @param simpleFn  
    一個普通函數(shù),這個普通函數(shù)無論如何都會被執(zhí)行。  

finally方法指定,不管Promise對象最后狀態(tài)如何,都會執(zhí)行的操作。

3. 代碼參考 3.1 finally()的實現(xiàn)
  Promise.prototype.finally = function( simpleFn ){
    let Pro = this.constructor
    return this.then(
      value => Pro.resolve( simpleFn() ).then( () => value ),
      error => Pro.resolve( simpleFn() ).then( () => { throw error } )
    )
  }
3.2 done()的實現(xiàn)
  Promise.prototype.done = function( resolveFn, rejectFn ){
    this
      .then( resolveFn, rejectFn )
      .catch( error => {
        // 這是一個把需要執(zhí)行的代碼,從任務隊列中拉出來的技巧
        setTimeout( () => { throw error }, 0)
      } )
  }

這兒使用了一個很常用的技巧:
我們來看一下這個例子:

  for(let i of [1,2,3]){
    setTimeout( () => { console.log( "setTimeout " + i ) }, 0)
    console.log( "console " + i )
  }

最終結果是:

  > console 1  
  > console 2  
  > console 3  
  > undefined  
  > setTimeout 1  
  > setTimeout 2  
  > setTimeout 3  

javascript除了維護著當前任務隊列,還維護著一個setTimeout隊列。所有未被執(zhí)行的setTimeout任務,會按順序放到setTimeout隊列中,等待普通任務隊列中的任務執(zhí)行完,才開始按順序執(zhí)行積累在setTimeout中的任務。
簡而言之, javascript會在執(zhí)行完當前任務隊列中的任務后,再執(zhí)行setTimeout隊列中的任務 。
我們設置任務在0s后執(zhí)行,可以將該任務調到setTimeout隊列中,延遲該任務發(fā)生,使之異步執(zhí)行。
這是異步執(zhí)行方案當中,最常用,也最省時省事的一種方式。

3.3 加載圖片
  function preloadImage(path){
    return new Promise( (resolve, reject) => {
      let img = document.createElement("img")
      img.style.display = "none"
      document.body.appendChild(img)
      // 當圖片加載完成后,promise轉為完成狀態(tài)
      // 此時,我們可以把該節(jié)點的圖片加載在應有的地方,并且將其刪除
      img.addEventListener("load", resolve)
      // 當圖片加載出錯后,promise轉為失敗狀態(tài)
      img.addEventListener("error", reject)
      img.src = path
    } )
  }
3.4 Generator與Promise聯(lián)合
  // Promise的包裝函數(shù) getFoo()
  function getFoo(){
    // ......something
    return new Promise( (resolve, reject) => {
      // ......something
      resolve("foo")
    } )
  }
  // Generator函數(shù) generator()
  function* generator(){
    try{
      let foo = yield getFoo()
      console.log(foo)
    }
    catch(error){
      console.log(error)
    }
  }
  // 自動執(zhí)行generator函數(shù)的函數(shù),現(xiàn)在可以用async語法替代它
  function run(generator){
    // 讓generator函數(shù)運行至第一個yield語句前,
    // 并獲得getFoo()的結果---一個promise函數(shù)
    let it = generator()
    function go(result){
      if(result.done) return result.value
      return result.value.then( value => {
          // 利用尾遞歸來實現(xiàn)自動執(zhí)行,讓本次遞歸產生的棧單元項只有一個
          return go( it.next(value) )
        }, error => {
          return go( it.throw(error) )
        }
      )
    }
    go(it.next())
  }
  // 調用run方法
  run(generator)
本文更新信息

易杭 2017/4/24 23:10 11840字

本文作者信息

易杭 歡迎大家參觀我的博客和我的Github

支持網站列表

易杭網 [www.freeedit.cn]

本文知識參考

ES6標準入門第二版(阮一峰)

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

轉載請注明本文地址:http://m.hztianpu.com/yun/82935.html

相關文章

  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • ES6Promise:要優(yōu)雅,也要浪漫

    摘要:就算改變已經發(fā)生了,即使再對對象添加回調函數(shù),也會立即得到這個結果。方法接收個參數(shù),第一個參數(shù)是狀態(tài)的回調函數(shù),第二個參數(shù)可選是狀態(tài)的回調函數(shù)。簡單來講,就是能把原來的回調寫法分離出來,在異步操作執(zhí)行完后,用鏈式調用的方式執(zhí)行回調函數(shù)。 在ECMAScript 6標準中,Promise被正式列為規(guī)范,Promise,字面意思就是許諾,承諾,嘿,聽著是不是很浪漫的說?我們來探究一下這個浪...

    weizx 評論0 收藏0
  • 異步和promise

    摘要:首先構造函數(shù)中需要有一些狀態(tài)和方法因為執(zhí)行實例邏輯的時候需要這些維護好的狀態(tài)和值其中著重提醒的就是的狀態(tài)機是單向的且狀態(tài)單向不可逆。 引言 16年時在公司分享過一次promise,猶記得當時是第一次分享,還蠻緊張的,當時分享的主要是promise的使用和基本原理,后來又給無線部門同學分享了一次。現(xiàn)在回顧想想,其實講的不是很完美,因為我當時的實現(xiàn)方式類似于簡化版q庫的實現(xiàn),考慮的也不全面...

    libxd 評論0 收藏0
  • H5學習

    摘要:為此決定自研一個富文本編輯器。本文,主要介紹如何實現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設備之間的。 對ES6Generator函數(shù)的理解 Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同。 JavaScript 設計模式 ② 巧用工廠模式和創(chuàng)建者模式 我為什么把他們兩個放在一起講?我覺得這兩個設計模式有相似之處,有時候會一個設計模式不能滿...

    aristark 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<