Promise知一二

冰大墩發表於2022-12-03

Promise

1.Promise的前置知識

  • 程式(廠房)

    • 程式的執行環境
  • 執行緒(工人)

  • 執行緒是實際進行運算的東西

  • 同步

    • 通常情況程式碼都是自上向下一行一行執行的
    • 前邊的程式碼不執行後邊的程式碼也不會執行
    • 同步的程式碼執行會出現阻塞的情況
    • 一行程式碼執行慢會影響到整個程式的執行
  • 解決同步的問題:

    • java python

      • 透過多執行緒來解決,但是一般消耗資源比較多
    • node.js

      • 透過非同步方式來解決

        我們可以這麼理解:客人就好比我們請求的資料,服務員就好比客戶端,廚師就好比伺服器,我們現在客人點菜,服務員接收到菜的名稱資訊,給廚師說,廚師開始做,廚師在做的時候,客人一直等,不能幹其他的事情,這就是同步,只能幹一件事,我們現在利用非同步的方式,可以讓客人在課桌上等著菜來,也不影響服務員接收下一個客人的點菜,這樣就可以很好的處理同步所帶來的堵塞問題

  • 非同步

    • 一段程式碼的執行不會影響到其他的程式
    • 非同步的問題:
  • 非同步的程式碼無法透過return來設定返回值

  • 特點:

    1. 不會阻塞其他程式碼的執行

    2. 需要透過回撥函式來返回結果

      function sum(a, b, cb) {
          setTimeout(() => {
              cb(a + b)  //呼叫箭頭函式,把結果作為回撥函式的引數
          }, 1000)
      }
      
      sum(123, 456, (result)=>{
          console.log(result)
      })
      
  • 基於回撥函式的非同步帶來的問題

    1. 程式碼的可讀性差

    2. 可除錯性差(造成回撥地獄)

      sum(123, 456, (result)=>{
          sum(result, 7, (result)=>{
              sum(result, 8, result => {
                  sum(result, 9, result => {
                      sum(result, 10, result => {
                          console.log(result)
                      })
                  })
              })
          })
      })
      
  • 解決問題:

    • 需要一個東西,可以代替回撥函式來給我們返回結果
    • Promise橫空出世
      • Promise是一個可以用來儲存資料的物件
        • Promise儲存資料的方式比較特殊,這種特殊的方式使得Promise可以用來儲存非同步呼叫的資料

2.Promise介紹

非同步呼叫必須要透過回撥函式來返回資料,當我們進行一些複雜的呼叫時,會出現回撥地獄

問題:

​ 非同步必須透過回撥函式來返回結果,回撥函式增加就不容易處理

  • Promise
    • Promise可以幫助我們解決非同步中的回撥函式的問題
    • Promise就是一個用來儲存資料的容器
      • 它擁有著一套特殊的儲存資料的方式
      • 這個方式使得它裡面可以儲存非同步呼叫的結果
  1. 建立Promise

    1. 建立Promise時,建構函式中需要一個函式作為引數

    2. Promise建構函式的回撥函式,它會在建立Promise時呼叫,呼叫時會有兩個引數傳遞進去

      const promise = new Promise((resolve, reject)=>{
          // resolve 和 reject 是兩個函式,透過這兩個函式可以向Promise中儲存資料
          // resolve 在執行正常的時候儲存資料, reject 是在執行錯誤的時候儲存資料
          resolve('我是正常執行的時候呼叫的')
          reject('我是錯誤執行的時候呼叫的')
          //透過函式來訪問Promise中新增資料,好處就是可以用來新增非同步呼叫的資料
          setTimeout(()=>{
              resolve('非同步中呼叫資料')
      	},2000)
          throw new Error('出錯了,呼叫的是reject')
      })
      
  2. 從Promise中讀取資料

    1. 可以透過Promise的例項方法then來讀取Promise中儲存的資料

    2. then需要兩個回撥作為引數,回撥函式來獲取Promise中的資料

      1. 透過resolve儲存的資料,會呼叫第一函式返回,可以在第一個函式中編寫處理資料的程式碼

      2. 透過reject儲存資料或者出現異常時,會呼叫第二個函式返回,可以在第二個函式中編寫處理異常的程式碼

        promise.then((result)=>{
            console.log('1',result)
        },(reson)=>{
            console.log('2',reason)
        })
        
  3. Promise中維護了兩個隱藏屬性:

    1. PromiseResult
      1. 用來儲存資料
    2. PromiseState
      1. 記錄Promise的狀態(三種狀態)
        1. pending(進行中)
        2. fulfilled(完成)透過resolve儲存資料時
        3. rejected(拒絕,出錯了)出錯了或透過reject儲存資料時
      2. state只能修改一次,修改以後永遠不會在變
    3. 流程:
      1. 當Promise建立時,PromiseState初始值為pending
        1. 當透過resolve儲存資料時 PromiseState 變為fulfilled(完成)
          1. PromiseResult變為儲存的資料
        2. 當透過reject儲存資料或出錯時 Promise 變為rejected(拒絕)
          1. PromiseResult變為儲存的資料 或 異常物件
      2. 當我們透過then讀取資料時,相當於為Promise設定了回撥函式
        1. 如果PromiseState變為fulfilled,則呼叫then的第一個回撥函式來返回資料
        2. 如果PromiseState變為rejected。則呼叫then的第二個回撥函式來返回資料
    const promise2 = new Promise((resolve, reject) => {
        resolve("哈哈")
    })
    
    // console.log(promise2)
    promise2.then(result => {
        console.log(result)
    }, reason => {
        console.log("出錯了")
    })
    
  4. catch()用法和then類似,但是隻需要一個回撥函式作為引數

    1. catch() 中的回撥只會在Promise被拒絕時才會呼叫

    2. catch() 相當於 then(null, reason=>{})

    3. catch() 就是一個專門處理Promise異常的方法

      promise2.catch(reason => {
          console.log(222222)
      })
      
  5. finally()

    1. 無論是正常儲存資料還是出現異常了,finally總會執行

    2. 但是finally的回撥函式中不會接收到資料

    3. finally()通常用來編寫一些無論成功與否都要執行的程式碼

      promise2.finally(()=>{
          console.log("沒有什麼能夠阻擋我執行的!")
      })
      

3.Promise詳解

3.1Promise用法

Promise就是一個用來儲存資料物件,但是由於Promise存取的方式的特殊,所以可以直接將非同步呼叫的結果儲存到Promise中

function sum(a, b) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(a + b)
        }, 1000)
    })
}
//回撥地獄式寫法:
sum(123, 456).then(result => {
    sum(result, 7).then(result =>{
        sum(result, 8).then(result => {
            console.log(result)
        })
    })
})
//promise寫法:
sum(123, 456)
    .then(result => result + 7)
    .then(result => result + 8)
    .then(result => console.log(result))

下來我們就解釋一下為什麼是這種鏈式呼叫的形式:

​ promise中的then方法會返回一個新的Promise,而我們接收並且輸出的是Promise的PromiseResult的值,就像這樣:then (return new Promise()),

Promise中會儲存回撥函式的返回值,當前的引數是上一個鏈式呼叫的返回值

promise
    .then(result => {
        console.log("回撥函式", result) 
        return "會作為下個then的引數"
    })
    .then(result => {
        console.log("第二個then", result)  //第二個then,會作為下個then的引數
        return "taotao真快樂"
    })
    .then(result => {
        console.log(result)  //taotao真快樂
    })

promise中的

  1. then (return new Promise())
  2. catch
  3. 這三個方法都會返回一個新的Promise, Promise中會儲存回撥函式的返回值
  4. finally
    1. finally的返回值,不會儲存到新的Promise中
  5. 對Promise進行鏈式呼叫時,後面的方法(then和catch)讀取的上一步的執行結果
    1. 如果上一步執行結果不是當前想要的結果,則跳過當前的方法,執行下一個方法
    2. 一般都把catch寫到最後,只寫一個,最後統一處理異常
    3. 當Promise出現異常時,而整個呼叫鏈中沒有catch,則異常會向外丟擲

3.2Promise靜態方法

  1. Promise.resolve() 建立一個立即完成的Promise

    1. Promise.resolve('成功呼叫時的資料').then(
          (result)=>{
              console.log(1111)
          })
      //相當於
      new Promise((resolve, reject)=>{
      	resolve('成功呼叫時的資料')
      }).then(result=>{
      	  console.log(1111)
      })
      
  2. Promise.reject() 建立一個立即拒絕的Promise

    1. Promise.reject('錯誤')
      
  3. Promise.all([...]) 同時返回多個Promise的執行結果

    1. 其中有一個報錯,就返回錯誤

    2. function sum(a, b){
          return new Promise((resolve, reject)=>{
             setTimeout(()=>{
                  resolve(a + b)
             },1000)
      	})
      }
      //傳遞一個可迭代物件(類陣列)
      Promise.all(
      	sum(111, 222),
          Promise.reject("哈哈"),
          sum(222, 333),
      	sum(333, 444)
      ).then((result)=>{
          console.log(result)  //[ 579, 11, 77 ]
      }).catch ((reason)=>{
          console.log(reason)  //'哈哈'
      })
      
  4. Promise.allSettled([...]) 同時返回多個Promise的執行結果(無論成功或失敗)

    1. {status: 'fulfilled', value: 579}

    2. {status: 'rejected', reason: '哈哈'}

    3. Promise.allSettled([
          sum(123, 456),
          sum(5, 6),
          Promise.reject("哈哈"),
          sum(33, 44)
      ]).then(r => {
          console.log(r)
      })
      
      //返回的結果如下:
      [
        { status: 'fulfilled', value: 579 },
        { status: 'fulfilled', value: 11 },
        { status: 'rejected', reason: '哈哈' },
        { status: 'fulfilled', value: 77 }
      ]
      
      
  5. Promise.race([...]) 返回執行最快的Promise(不考慮對錯)

    1. Promise.race([
          Promise.reject(1111),
          sum(123, 456),
          sum(5, 6),
          sum(33, 44)
      ]).then(r => {
          console.log(r)
      }).catch(r=>{
          console.log(r)
      })
      //執行結果如下
      1111
      Promise.reject(1111),不用等定時器執行結束,直接就呼叫
      
  6. Promise.any([...]) 返回執行最快的完成的Promise

    1. Promise.any([
          Promise.reject(1111),
          Promise.reject(2222),
          Promise.reject(3333),
      ]).then(r => {
          console.log(r)
      }).catch(r => {
          console.log("錯誤", r)
      })
      

相關文章