等待 async/await 结果返回,以便使用这些值呈现动态 html
Posted
技术标签:
【中文标题】等待 async/await 结果返回,以便使用这些值呈现动态 html【英文标题】:Wait for async/await results to return in order to render dynamic html with those values 【发布时间】:2020-12-14 06:34:00 【问题描述】:几天前我发布了一个关于此的问题,我得到的建议似乎部分有效,返回了我想要的结果,但在最终呈现数据时,我遇到了我的上一个问题。如果您想参考它,这是原始帖子: Grab the value of a span element
我使用 Fetch 重写了我的所有代码,而不是旧的 xmlHttp API,因为这是在上一篇文章中推荐的。为了便于阅读,我还想将这些函数分解为其他函数。
您会看到 fetch API 正在调用另一个数据库(出于隐私原因,我不得不隐藏标题)并调用 callFetch
函数。 callFetch
函数本质上采用 data
对象并通过 Google 的 distanceMatrix API 运行来自 data
的地址,以便将用户的输入地址(与 addresses
数组重复相同的次数)与data
对象。在getDistance
内部,我得到了所有比较结果并将结果推送到单独的数组中。
在getSortedValues
内部,我从getDistance
获取结果,按里程排序(这是我要在渲染数据中返回的更重要的部分之一),然后将它们作为单独的对象传递(facility
, distance
, time
, address
) 到一个名为combined
的数组。
最后,renderTemplate
使用每个组合对象的值中的值组合并呈现 html,并动态呈现到页面。
我更新了我的一些代码,并试图澄清更多。按钮要求我添加模拟值,因为用户无法测试代码。我试过了,但它改变了我的代码的行为,并取消了从另一个不再存在等待问题的来源提取数据的动态部分。
我相信我的主要问题在于sortValues
。当我点击提交按钮(我向每个函数添加控制台日志以更好地了解函数触发的顺序)时,您将看到所有内容第一次触发(在图像中),sortValues 返回一个空数组。第二次数组填充所有四个对象,每个对象有 15 个值。所以,我相信,无论我将async
添加到renderTemplate
和await
ing sortValues
,sortValues
第一次仍然会返回一个空数组。我认为这一切的根源在于等待combined
填充到sortValues
内部。有谁知道我可以做些什么来等待或让combined
在第一次调用sortValues
时填充?
let inputVal = [];
let facilityArray = [];
let distanceArray = [];
let timeArray = [];
let filteredArray = [];
let sortedArray = [];
let combined = [];
let facAddress = [];
let resultsObject, intDistanceResult;
const resetButton = document.querySelector("#refresh");
// Set data object
const data =
from: "xxxxxxxxx",
// Facility id and address of each location
select: [6, 8],
options:
skip: 0,
top: 0,
,
;
fetchAPI = () =>
fetch("https://api.quickbase.com/v1/records/query",
method: "POST",
headers:
"QB-Realm-Hostname": "xxxxx.quickbase.com",
"Authorization": "QB-USER-TOKEN xxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
,
body: JSON.stringify(data),
)
.then(resp => resp.json())
.then(data => callFetch(data))
.catch(error => console.log("Error:", error));
console.log("fetchAPI fired")
;
callFetch = dataObject =>
console.log("callFetch fired");
// Map through the data object and grab the facId and address of each location
const facId = dataObject.data.map(e => e["6"].value);
const addresses = dataObject.data.map(e => e["8"].value);
// Create Google Maps distance service instance
distanceService = new google.maps.DistanceMatrixService();
for (let i = 0; i < addresses.length; i++)
// Add matrix settings object
distanceService.getDistanceMatrix(
origins: [inputVal.toString("")],
destinations: [addresses[i]],
travelMode: "DRIVING",
unitSystem: google.maps.UnitSystem.IMPERIAL,
durationInTraffic: true,
avoidHighways: false,
avoidTolls: false,
,
// Set response and error capture
(response, status) =>
if (status !== google.maps.DistanceMatrixStatus.OK)
console.log("Error:", status);
const message = document.querySelector("#message");
message.innerHTML = `Error: $status. Please resubmit.`;
else
const distanceResult = response.rows[0].elements[0].distance
.text;
const timeResult = response.rows[0].elements[0].duration.text;
// Convert distanceResult values to integers and push to distanceArray to later sort
intDistanceResult = parseInt(distanceResult.replace(/,/g, ""));
// Push results to respective arrays to pass to combined object inside of sortValues function
facilityArray.push(facId[i]);
distanceArray.push(intDistanceResult);
timeArray.push(timeResult);
facAddress.push(addresses);
);
renderTemplate();
;
sortValues = () =>
console.log("sortValues fired");
// Re-sort array of objects from getDistance function with values in order by mileage, ascending
combined = facilityArray
.map((facility, i) => (
facility,
distance: distanceArray[i],
time: timeArray[i],
address: facAddress[i]
))
.sort((first, second) => first.distance - second.distance);
console.log(combined);
;
renderTemplate = async() =>
await sortValues();
console.log("renderTemplate fired");
// Grab container div entry point in html
let container = document.querySelector(".container-sm");
for (let i = 0; i < combined.length; i++)
// Create html dynamically and populate based on combined' objects values
let div = document.createElement("div");
div.classList.add("d-inline-flex", "p-2", "mb-1");
div.innerHTML =
`<div class="container">
<div class="card" style="width: 20rem; height: fixed;">
<div class="card-body">
<input class="form-control" hidden value="$inputVal.join('')" type="text" placeholder="Destination Address" id="destaddress$i">
<input class="form-control" hidden readonly type="text" placeholder="Start Address" id="startaddress$i">
<h6 class="card-title" style="font-weight: bold">Service Center - $combined[i].facility</h6>
<h6 class="card-title">Distance - <span id="distance$i">$combined[i].distance miles</span></h6>
<h6 class="card-title">Drive Time - <span id="time$i">$combined[i].time</span></h6>
</div>
</div>
</div>`;
// Append new values to the div
container.appendChild(div);
form.addEventListener("submit", (e) =>
e.preventDefault();
const patientAddressInput = document.querySelector("#patientaddress");
// Get user entry and assign to inputVal in dynamically created HTML
inputVal.push(patientAddressInput.value);
fetchAPI();
console.log("Listener fired");
);
resetButton.addEventListener("click", () => location.reload());
<!DOCTYPE html>
<html>
<head>
<title>Ethos Service Center - Google Maps Distance Search</title>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body>
<div class="container-sm mt-3">
<form class="form mb-3" id="form">
<div class="search">
<div class="card-body">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Patient Destination</span>
</div>
<input class="form-control" type="text" placeholder="Enter Zip Code" class="patientaddress" id="patientaddress" required>
<div class="input-group-prepend">
<span class="input-group-text" id="message"></span>
</div>
</div>
<hr>
<button class="btn btn-primary mt-2" type="submit" id="submit">Submit</button>
<button class="btn btn-outline-success mt-2 ml-3" type="reset" value="Reset" id="refresh">Clear Destination</button>
</div>
</div>
</form>
</div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places&key=[Google Maps API Key]"></script>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
【问题讨论】:
似乎fetchAPI()
函数应该是await
ed。 renderTemplate
函数没有做任何异步操作。带有模拟数据的工作代码示例会很棒。我不清楚。您可以尝试将代码调整为await fetchAPI();
,然后调用getSortedValues()
函数,因为现在所有变量都已到位。
谢谢。我将致力于添加模拟数据,因为我必须排除其中一些私钥。
@buttons 使用带有then
而不是await
的fetch api 承诺完全没问题。
您的fetch
电话和承诺链都很好。问题不在于renderTemplate
或sortValues
——它们可以工作(并且是同步的,你不应该将await
与它们一起使用)。问题实际上是异步 distanceService.getDistanceMatrix
调用 - 您试图在这些调用完成之前呈现(仍然是空的)模板。
@Ang 将getDistanceMatrix
提取到辅助函数和make that return a promise 中。然后将await
它们放在你的循环中,或者一次创建所有承诺,将它们放在一个数组中,然后使用Promise.all
,你可以使用await
(或将.then()
链接到),此时你可以渲染结果。
【参考方案1】:
我找到了一种方法来做到这一点,尽管这是一种老套、俗气的方法,而且可能不是最有效的方法。我在for loop
的末尾添加了一个三元运算符,用于检查i
的值是否等于数组中最后一个元素的索引值。如果是,请等待 1 秒(通过运行 setTimeout 函数)等待 DistanceMatrix 调用运行,以便我有一些值,然后调用 sortValues
函数。它至少有效。所有的代码都是一样的,但是这里是添加了三元运算符的callFetch
函数和setTimeout
函数。
callFetch = async dataObject =>
console.log("callFetch fired");
// Map through the data object and grab the facId and address of each location
const facId = dataObject.data.map(e => e["6"].value);
const addresses = dataObject.data.map(e => e["8"].value);
// Create Google Maps distance service instance
distanceService = new google.maps.DistanceMatrixService();
for (let i = 0; i < addresses.length; i++)
// Add matrix settings object
distanceService.getDistanceMatrix(
origins: [inputVal.toString("")],
destinations: [addresses[i]],
travelMode: "DRIVING",
unitSystem: google.maps.UnitSystem.IMPERIAL,
durationInTraffic: true,
avoidHighways: false,
avoidTolls: false,
,
// Set response and error capture
(response, status) =>
if (status !== google.maps.DistanceMatrixStatus.OK)
console.log("Error:", status);
const message = document.querySelector("#message");
message.innerHTML = `Error: $status. Please resubmit.`;
else
distanceResult = response.rows[0].elements[0].distance.text;
timeResult = response.rows[0].elements[0].duration.text;
// Convert distanceResult values to integers and push to distanceArray to later sort
intDistanceResult = parseInt(distanceResult.replace(/,/g, ""));
// Push results to respective arrays to pass to combined object inside of sortValues function
facilityArray.push(facId[i]);
distanceArray.push(intDistanceResult);
timeArray.push(timeResult);
facAddress.push(addresses);
);
(i == addresses.length - 1 ? setTimeout(() => sortValues() , 1000) : null)
;
【讨论】:
以上是关于等待 async/await 结果返回,以便使用这些值呈现动态 html的主要内容,如果未能解决你的问题,请参考以下文章