Асинхронное значение Javascript не передается при следующем вызове функции
У меня есть этот метод 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 в других местах, но переданные в computeValueByFormula formData не обновляются. Даже если я передам это непосредственно без оператора распространения.
Это дает мне пустое поле в форме, а в formData у меня есть значение.
Примечание: рекурсивная форма функций, необходимых для оценки элементов поля массива.
1 ответ
Вы решаете lookUpValue
функционировать сразу с newFormData
вернувшись Promise.resolve(newFormData)
, Это не будет ждать каких-либо асинхронных обратных вызовов внутри вашего Object.keys(...).forEach(...)
-loop. Как lookUpValue
имеет некоторые асинхронные вычисления, следует дождаться их завершения.
Если lookUpValue
был отмечен как async
Вы могли бы опустить async
Ключевое слово в Object.keys(...).forEach(key => {...})
цикл, и он должен ждать await
звонки.
Есть одно предостережение: он будет работать для каждого await
звонки последовательно, так что это может занять некоторое время. В этом случае может быть лучше создать несколько Обещаний и ожидать их параллельно через Promise.all
,
Использование параллельного подхода может привести к повышению производительности, но может также привести к ошибкам из-за условий гонки. Тем более, что вы проходите свое состояние рекурсивно, могут возникнуть дополнительные сложности.