【发布时间】: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 和awaiting 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()函数应该是awaited。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()链接到),此时你可以渲染结果。
标签: javascript async-await fetch-api