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)
})
复制代码

你可能感兴趣的:(es6异步方法在项目中的实践)