Асинхронное значение Javascript не передается при следующем вызове функции - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть этот метод onChange с соответствующими вызовами функций ниже для React-Jsonschema-Form.Проблема, с которой я сталкиваюсь, заключается в том, что newFormData при передаче в computeValueByFormula не передает асинхронное значение.

onChange = ({ schema, formData }) => {
    const { properties } = schema

    this.updateTotalCell(properties)

    lookUpValue(properties, formData).then(newFormData => {
        const newFormData2 = computeValueByFormula(properties, newFormData)
        this.setState({ formData: newFormData2 })
    })
}

// recursive function to do math calculation on string formula input
// use case: mathCalculation("1 * 2 + 4 / 2 - 6")
export function mathCalculation (formula) {
  const plusOperator = '+'
  const minusOperator = '-'
  const multiplyOperator = '*'
  const divideOperator = '/'

  if (formula.indexOf(plusOperator) > 0) {
    const operands = formula.split(plusOperator)
    let total = 0

    operands.forEach(operand => {
      total = total + mathCalculation(operand)
    })

    return total
  }

  else if (formula.indexOf(minusOperator) > 0) {
    const operands = formula.split(minusOperator)
    let total = 0

    operands.forEach((operand, index) => {
      if (index === 0) {
        total = mathCalculation(operand)
      } 
      else {
        total = total - mathCalculation(operand)
      }
    })

    return total
  }

  else if (formula.indexOf(multiplyOperator) > 0) {
    const operands = formula.split(multiplyOperator)
    let total = 1

    operands.forEach(operand => {
      total = total * mathCalculation(operand)
    })

    return total
  }

  else if (formula.indexOf(divideOperator) > 0) {
    const operands = formula.split(divideOperator)
    let total = 1

    operands.forEach((operand, index) => {
      if (index === 0) {
        total = mathCalculation(operand)
      }
      else {
        total = total / mathCalculation(operand)
      }
    })

    return total
  }

  return Number(formula)
}

// compute field value based on value of other fields
export function computeValueByFormula (properties, formData) {
  let newFormData = {...formData}

  Object.keys(properties).forEach(key => {
    if (properties[key].formula) {
      const formula = properties[key].formula

      let operands = formula.replace(/\+|-|\*|\//g, ' ').split(' ')
      operands = operands.map(operand => formData[operand])

      if (properties[key].type === 'number') {
        const operators = formula.replace(/\w/g, '').split('')
        const updatedFormula = operands.map(operand => operators.length > 0 ? operand + operators.shift() : operand).join('')
        newFormData[key] = mathCalculation(updatedFormula)
      }
      else if (properties[key].type === 'string'){
        newFormData[key] = operands.join(' ')
      }
    }
    else if (properties[key].type === 'array') {
      if (formData[key] !== undefined) {
        newFormData[key].forEach((item, childKey) => {
          newFormData[key][childKey] = computeValueByFormula(properties[key].items.properties, formData[key][childKey])
        })
      }
    }
  })

  return newFormData
}

// lookup value based on value of other field
export function lookUpValue (properties, formData, parentFieldName, parentFormData) {
  let newFormData = {...formData}

  Object.keys(properties).forEach(async (key) => {
    if (properties[key].lookup) {
      const { collection, field, parameterField } = properties[key].lookup

      if (parentFormData !== undefined) { // pattern is in array field item
        if (parameterField.indexOf(':') > 0) { // parsing array field item
          const arrayRef = parameterField.split(':')
          const arrayField = arrayRef[0]
          const itemField = arrayRef[1]

          if (arrayField === parentFieldName) {
            const lookupValue = formData[itemField]
            newFormData[key] = await axios.get(`${API_URL}/record-lookup?collection_id=${collection}&lookup_field=${itemField}&lookup_value=${lookupValue}&lookup_target_field=${field}`)
              .then(res => res.data.data)
          }
        } else {
          const lookupValue = parentFormData[parameterField]
          newFormData[key] = await axios.get(`${API_URL}/record-lookup?collection_id=${collection}&lookup_field=${parameterField}&lookup_value=${lookupValue}&lookup_target_field=${field}`)
            .then(res => res.data.data)
        }
      } else {
        const lookupValue = formData[parameterField]
        newFormData[key] = await axios.get(`${API_URL}/record-lookup?collection_id=${collection}&lookup_field=${parameterField}&lookup_value=${lookupValue}&lookup_target_field=${field}`)
          .then(res => res.data.data)
      }
    }
    else if (properties[key].type === 'array') {
      if (formData[key] !== undefined) {
        newFormData[key].forEach(async (item, childKey) => {
            newFormData[key][childKey] = await lookUpValue(properties[key].items.properties, formData[key][childKey], key, formData).then(data => data)
        })
      }
    }
  })

  return Promise.resolve(newFormData)
}

То, что я хочу сделать, - это поиск значения на основе пользовательского ввода в форме.Допустим, у меня есть таблица валют в базе данных, когда пользователь изменяет выпадающий выбор имени валюты, другое поле, в котором должен храниться курс валюты, будет выполнять серверный вызов для получения связанных данных валюты из базы данных.

Это полебудет иметь поле поиска на схеме.Так же, как и поле с математическим вычислением, поле со схемой будет иметь поле формулы.

Каким-то образом после вызова сервера он обновит formData в других местах, но formData, переданная в computeValueByFormula, не обновится.,Даже если я передаю его напрямую без оператора распространения.

Это дает мне пустое поле в форме, а в formData у меня есть значение.

Примечание: Рекурсивная форма необходимых функцийоценивать элементы поля массива.

1 Ответ

0 голосов
/ 12 февраля 2019

Вы сразу решаете функцию lookUpValue с помощью newFormData, возвращая Promise.resolve(newFormData).Это не будет ждать каких-либо асинхронных обратных вызовов, выполняемых внутри Object.keys(...).forEach(...) -loop.Поскольку lookUpValue имеет некоторые асинхронные вычисления, он должен дождаться их завершения.

Если lookUpValue помечен как async, вы можете опустить ключевое слово async в цикле Object.keys(...).forEach(key => {...}) ион должен ждать вызовов await.

Однако есть одно предупреждение: он будет запускать для каждого await вызовов последовательно , поэтому это может занять некоторое время.В этом случае, возможно, будет лучше создать несколько Обещаний и ожидать их параллельно через Promise.all.

. Использование параллельного подхода может привести к повышению производительности, но может также привести к ошибкам из-за условий гонки.Тем более, что вы проходите рекурсивное состояние, могут возникнуть дополнительные сложности.

...