es6異步方法在項目中的實踐

es6異步方法在項目中的實踐

author: teal.yao

polling

/// 輪詢

// promise version
function fetchProtocol_promise(token) {
    let counter = 0;
    return new Promise((resolve, reject) => {
        polling();

        function polling() {
            fetch().then(() => {
                if (data.status === 200) {
                    switch (data.result.state) {
                        case 2:
                            resolve(getPreviewURL(data.result.url))
                            break
                        case 1:
                            if (counter++ > 20) {
                                reject("合同建立超時,請從新再試")
                            }
                            // fetch again
                            setTimeout(polling, 300)
                            break
                        case 3:
                            reject("合同建立失敗")
                            break
                    }
                }
            })
        }
    });
}


/// async version
// 對比promise方式,會多了一個變量來管理狀態成功
// 利用了promise裏面的resolve接收的是一個promise,則會等待此promise的完成
async function fetch(token) {
    let counter = 0;
    return await polling();

    async function polling() {
        if (counter++ > 20) {
            return [false, "合同建立超時,請從新再試"]
        }
        let data = await fetch()
        if (data.status === 200) {
            switch (data.result.state) {
                case 2:
                    return [true, getPreviewURL(data.result.url)]
                case 1:
                    // fetch again
                    return new Promise(resolve =>
                        setTimeout(function () {
                            // waiting for polling state change
                            resolve(polling());
                        }, 300)
                    );
                case 3:
                    return [false, "合同建立失敗"]
            }
        }
    }
}
複製代碼

給promise設置timeout

/** * timeout promise * * promise沒有原生的超時的方法,得借用其餘方法去處理 * * 1. promise的兩個最終狀態, fulfilled、 rejected * * 2.下面借用Promise.race來處理超時 * * Promise.race(iterable),當iterable參數裏的任意一個子promise被成功或失敗後, * 父promise立刻也會用子promise的成功返回值或失敗詳情做爲參數調用父promise綁定的相應句柄, * 並返回該promise對象。 */

/// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const http = require('http')

let get = function (url) {
    return new Promise((resolve, reject) => {
        // 僞裝這裏不支持timeout參數
        http.get({
            url,
        }, res => {
            res.on('data', d => {
                resolve(d.toString())
            })
        })
    })
}

/// promise timeout + promise.race
function promiseTimeout(promise, ms) {
    let timeout = new Promise((resolve, reject) => {
        setTimeout(function () {
            reject('timeout')
        }, ms)
    })
    return Promise.race([promise, timeout])
}
promiseTimeout(get('http://github.com'), 5).then(text => {
    console.log({
        text
    })
}).catch(err => {
    console.log({
        err
    })
})
複製代碼

回調地獄的解決方案

/** * ======解決回調地獄的========= * * 經常使用的回調套回調,容易出現回調地獄,形成代碼的可讀性、維護性差 * * 現藉助promise來解決 */


  class Form {
      constructor(num, errMsg) {
          this.num = num
          this.errMsg = errMsg
      }
      validator(callback) {
          callback(this.num < .4)
      }
  }
  let form1 = new Form(.3, 'im .3')
  let form2 = new Form(.7, 'im .7')
  let form3 = new Form(.6, 'im .6')

  // 1. 回調地獄寫法
  // 回調以後仍是回調,無窮無盡
  form1.validator(valid => {
      if (valid) {
          form2.validator(valid => {
              if (valid) {
                  form3.validator(valid => {
                      if (valid) {
                          console.log({
                              valid
                          })
                      } else {
                          console.log({
                              err: form3.errMsg
                          })
                      }
                  })
              } else {
                  console.log({
                      err: form2.errMsg
                  })
              }
          })
      } else {
          console.log({
              err: form1.errMsg
          })
      }
  })

  // 2. use async/await
  // 把callback包裝成promise,結合await同步寫法
  // 提升代碼的可讀性
  let formSerialValidator = async function (forms) {
      let valid = true
      let errMsg = ''
      // 用for來模擬表單的線性調用 
      for (let form of forms) {
          // 利用await的同步寫法
          // 接收promise的resolve傳值,或reject的傳值
          // 保持 resolve/reject 傳值結構是同樣的: tuple [boolean, string]
          [valid, errMsg] = await promiseFactory(form)
          // .catch(errMsg => {
          // // 保持與resolve返回結果一致
          // return [false, errMsg]
          // })
          if (!valid) {
              break
          }
      }
      return [valid, errMsg]

      // 將回調包裝成promise
      function promiseFactory(form) {
          return new Promise((resolve, reject) => {
              form.validator(valid => {
                  // if (valid) {
                  resolve([valid, form.errMsg])
                  // }
                  // reject(form.errMsg)
              })
          })
      }
  }
  formSerialValidator([form1, form2, form3]).then(([valid, errMsg]) => {
      console.log({
          valid,
          errMsg
      })
  })


  // 3. promise + reduce
  // 利用then的鏈式調用
  // a().then(() => new Promise()).then(() => new Promise()) 第一個then爲reject時,整個鏈的狀態就是reject
  let reduceValidator = function (forms) {
      // 用reduce生成校驗的Promise chain

      // reduce的初始initValue得注意下,是Promise.resolve()
      // Promise.resolve()傳遞的是一個具體的值(undefined),因此狀態爲fulfilled,可直接使用then調用
      // Promise then方法返回的promise,則等待該promise的返回
      return forms.reduce((promise, form) => {
          return promise.then($ => {
              return new Promise((resolve, reject) => {
                  form.validator(valid => {
                      console.log(valid)
                      // resolve 只能有一個參數
                      if (valid) {
                          resolve(true)
                      }
                      reject(form.errMsg)
                  })
              })
          })
      }, Promise.resolve())
  }
  reduceValidator([form1, form2, form3]).then(valid => {
      console.log({
          valid,
      })
  }).catch(errMsg => {
      console.log({
          errMsg
      })
  })

  // Promise then方法返回的promise,則等待該promise的返回
  Promise.resolve(new Promise((resolve, reject) => {
      resolve(1)
  })).then(res => {
      console.log(res)
  })
複製代碼

表單驗證的yield寫法

/// 傳統表單的校驗

 // 通常人的寫法
 // this.$message.error重複調用,不利於維護
 function validator1() {
     let msg = ''
     if (fd.displayPosition === null) {
         this.$message.error('請選擇發送端')
         // return false
     } else if (fd.type === null) {
         this.$message.error('請選擇通知方式')
         // return false
     } else if (fd.peroid === undefined || JSON.stringify(fd.peroid) === '[null,null]') {
         this.$message.error('請選擇有效日期')
         // return false
     } else if (self.isShowNum && fd.showNum === null) {
         msg = '請選擇彈出次數'
         // return false
     }
     this.$message.error(msg)
     return true
 }

 // 二般人的寫法
 // 利用yield給暫停一下
 // generator函數調用後生成一個Iterator對象,返回的done爲true時結束
 // Iterator: {
 // next() {
 // return {
 // value: any,
 // done: boolean
 // }
 // }
 // }
 //
 // for..of 實現了Iterator接口,能夠調用generator函數的生成對象
 function validator2() {
     function* check() {
         if (fd.displayPosition === null) {
             yield '請選擇發送端'
         }
         if (fd.type === null) {
             yield '請選擇通知方式'
         }
         if (fd.peroid === undefined || JSON.stringify(fd.peroid) === '[null,null]') {
             yield '請選擇有效日期'
         }
         if (self.isShowNum && fd.showNum === null) {
             yield '請選擇彈出次數'
         }
     }
     let iterator = check()
     for (let msg of iterator) {
         // if (msg !== undefined) {
         this.$message.error(msg)
         return false
         // }
     }
     return true
 }
複製代碼

promise/async的錯誤處理

/// 1. promise內部的錯誤,不會中斷外部的執行
new Promise((resolve, reject) => {
    throw Error('promise')
    console.log(1)
})
console.log('after promise error')
// 可以使用try-catch或Promise.prototype.catch處理
new Promise((resolve, reject) => {
    try {
        throw Error('promise')
    } catch (err) {
        console.log(err)
    }
})
console.log('after promise error')

// 與上面的捕獲錯誤的方法對比,throw Error後面的代碼將沒法執行
new Promise((resolve, reject) => {
        throw Error('promise')
        console.log('after inner error')
    })
    .catch(err => {
        console.log(err)
        // return 'form catch'
        throw Error('from catch')
    })
    .catch(res => {
        console.log(res)
    })

console.log('after promise error')


/// 2. async函數的錯誤處理
// 處理await返回的錯誤,promise不捕獲,則留給async函數處理
// promise前面加await與不加的區別
// 觀察下面3種狀況:
// 1. 內部promise錯誤不處理
// 2. 內部promise加上catch
// 3. 內部promise接上await 
async function usePromiseCatch() {
    new Promise((resolve, reject) => {
        throw Error('throw')
        console.log(330)
    })
    // .catch(err => {
    // console.log(err)
    // })
    console.log('continue')
}

usePromiseCatch().catch(err => {
    console.log('in async catch', err)
})
複製代碼
相關文章
相關標籤/搜索