使用await解决异步问题的注意点总结

项目中经常需要根据接口的返回值决定下一步操作promise, async/await时比较常见的处理异步操作的方法。本文主要是结合自己当前再项目中使用async/await的场景,说明在使用过程中应该注意的问题。

1、await命令后面的promise对象的运行结果可能是rejected, 所以最好把await命令放在try···catch代码块中。

// 好的方法,await在try···catch代码块中
    async handleOk (type) {
      try {
        this.btnLoading = true
        const res = await this.confirmDanger(type)
        this.btnLoading = false
        if (res.code === '0') {
          this.handle_remark = ''
          this.expert_check = '否'
          this.$emit('updateAlarmList', 'ok')
        }
      } catch (e) {
        this.btnLoading = false
        console.log(e)
      }
    }
// 糟糕的方法,reject的情况下,没有异常处理
    async handleOk (type) {
      this.btnLoading = true
      const res = await this.confirmDanger(type)
      this.btnLoading = false
      if (res.code === '0') {
        this.handle_remark = ''
        this.expert_check = '否'
        this.$emit('updateAlarmList', 'ok')
      }
    },

如上代码是常见的点击确认按钮进行的处理操作,为防止多次点击,给按钮增加了loading,如果await命令不在try···catch代码块,promise运行结果为rejected的情况下,不会执行await后面的代码,没有对异常情况进行处理,按钮就会一直在loading状态。

2、多个await如果不存在继发关系,最好让他们同时触发

// 好的方法,两个接口同时触发
    async handleOk () {
      try {
        this.btnLoading = true
        const [res1, res2] = await Promise.all([this.handleAlarm(), this.updateAlarm()])
        this.btnLoading = false
        if (res1.code === '0' && res2.code === '0') {
          this.remark = ''
          this.handleStatus = 1
          this.$emit('updateAlarmList', 'ok')
        }
      } catch (e) {
        this.btnLoading = false
        console.log(e)
      }
    },
 // 糟糕的方法, 两个接口继发执行,比较耗时
    async handleOk () {
      try {
        this.btnLoading = true
        const res1 = await this.handleAlarm()
        const res2 = await this.updateAlarm()
        this.btnLoading = false
        if (res1.code === '0' && res2.code === '0') {
          this.remark = ''
          this.handleStatus = 1
          this.$emit('updateAlarmList', 'ok')
        }
      } catch (e) {
        this.btnLoading = false
        console.log(e)
      }
    },

如上代码中,点击确认按钮,会调用两个接口,根据两个接口的返回结果进行后续操作。这两个接口之前不存在继发关系,因此,好的方法是采用Promise.all使两个接口可以同时触发。

3、await命令只能用在async函数中,在使用迭代器处理时需要注意

    async getImageUrl () {
      const textPromises = this.picUrlList.map(el => {
        await this.imageRedirect(el.fullUrl) // 报错,await用在普通函数
      })
    },

上面的函数会报错,因为将await用在普通的函数中了,但是,如果将map方法改成async函数,也会有问题。

    getImageUrl () { // 这里不需要async
      const textPromises = this.picUrlList.map(async el => { // 这里使用async
        await this.imageRedirect(el.fullUrl)
      })
    },

上面的代码可能不会正常工作,因为此时的请求会并发执行,而不是继发执行。

如果想要多个请求继发执行,正确的方法是采用for循环,如下所示

    async getImageUrl () {
      for (let pic of picUrlList) {
        await this.imageRedirect(pic.fullUrl)
      }
    },

如果确实希望多个请求并发执行,使用Promise.all方法,如下所示,下面两种方法效果相同

方法一
    async getImageUrl () {
      const textPromises = this.picUrlList.(el => {
        const { data } = this.imageRedirect(el.fullUrl)
        return data
      })
      this.urls = await Promise.all(textPromise)
    },
方法二
    async getImageUrl () {
      const textPromises = this.picUrlList.(el => {
        const { data } = this.imageRedirect(el.fullUrl)
        return data
      })
      this.urls = []
      for (const textPromise of textPromises) {
        this.urls.push({
          url: await textPromise
        })
      }
    },

以上是在项目中使用await需要注意事项,包含了异常处理,多个异步操作并发执行、继发执行的场景,掌握这些注意事项,可以解决大部分异步出问题。

更多推荐