在 .map() 中调用 ajax

Make an ajax call inside of a .map()

我正在升级 jquery,发现“async: false”选项已被弃用。这是有道理的,在 99.9% 的情况下我同意这个基本原理,但我有一个案例我认为我真的需要它而且我无法在我的生活中弄清楚如何使用纯异步 ajax 无论我如何使用 promises 或 async/await.

都会调用

我的用例在一个 Vue 组件中,我有一组联系人。我需要做的是映射联系人并验证它们。一种此类验证需要通过“check_email”ajax 端点快速检查电子邮件有效性。

验证(或不验证)列表后,我会提交列表(如果有效)或显示错误消息(如果无效)。

我的代码是这样的

sendContacts: function() {
   valid = this.validateContacts()

   if (valid) {
       // send the contacts
   } else {
      return // will display error messages on contacts objects
   }
},

validateContacts: function() {
    this.contacts = this.contacts.map((contact) => {
          if (!contact.name) {
            contact.validDetails.name = false
            contact.valid = false
            return contact
          }
          if (!contact.email) {
            contact.validDetails.emailExists = false
            contact.valid = false
            return contact
          } 
          if (!check_email(email)) { // THIS IS ASYNC NOW WHAT DO I DO
            contact.valid = false
            contact.validDetails.emailFormat = false
          }
          return contact
    }
     var validData = this.contacts.map(c => {
          return c.valid
     })
     return !validData.includes(false)
}
function check_email(email) {
  const url = `/api/v1/users/check-email?email=${email}`
  let valid = false
  $.ajax({
    url: url,
    type: 'POST',
    async: false, // I can't do this anymore
    headers: {
      'X-CSRFToken': csrfToken
    },
    success: resp => {
      valid = true
    },
    error: err => {
    }
  })
  return valid
}

我的数据函数:

data: function() {
    return {
        contacts: [this.initContact()],
        showThanks: false,
        emailError: false,
        blankEmail: false,
        blankName: false
    }
  },
methods: {
    initContact: function() {
        return {
          name: null,
          email: null,
          title: null,
          validDetails: this.initValidDetails(),
          valid: true,
        }
    },
    initValidDetails: function() {
      return {
        emailDomain: true,
        emailExists: true,
        emailFormat: true,
        name: true
      }
    }
}

同样,我在我能想到的每个地方都尝试了 async/await,但我无法正确验证它,然后执行关于函数的发送联系人函数部分是否应该触发的正确逻辑。请帮忙!

一旦验证的任何部分是异步的,您就必须将整个事情都视为异步的。这包括在 sendContacts.

中调用 validateContacts

首先,您应该将 check_email 更改为 return Promise<bool>。在 Vue 项目中包含 jQuery 通常不是一个好主意,所以让我们使用 fetch 代替(Axios 是另一种流行的选择)。

async function check_email(email) {
  const params = new URLSearchParams({ email })
  const res = await fetch(`/api/v1/users/check-email?${params}`, {
    method: 'POST',
    headers: {
      'X-CSRFToken': csrfToken
    }
  })
  return res.ok
}

至于您的异步验证逻辑,最好将您的联系人映射到一组承诺并使用 Promise.all 等待它们。

async validateContacts () {
  const validationPromises = this.contacts.map(async contact => {
    if (!contact.name) {
      return {
        ...contact,
        valid: false,
        validDetails: {
          ...contact.validDetails,
          name: false
        }
      }
    }
    if (!contact.email) {
      return {
        ...contact,
        valid: false,
        validDetails: {
          ...contact.validDetails,
          emailExists: false
        }
      }
    } 
    if (await check_email(contact.email)) { // await here
      return {
        ...contact,
        valid: false,
        validDetails: {
          ...contact.validDetails,
          emailFormat: false
        }
      }
    }
    return { ...contact, valid: true }
  })

  // now wait for all promises to resolve and check for any "false" values
  this.contacts = await Promise.all(validationPromises)
  return this.contacts.every(({ valid }) => valid)
}

如前所述,现在您需要在 sendContacts

中异步处理它
async sendContacts () {
   if (await this.validateContacts()) {
     // send the contacts
   }
}