等待 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 添加到renderTemplateawaiting sortValuessortValues 第一次仍然会返回一个空数组。我认为这一切的根源在于等待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 电话和承诺链都很好。问题不在于renderTemplatesortValues ——它们可以工作(并且是同步的,你不应该将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的主要内容,如果未能解决你的问题,请参考以下文章

Task和async/await详解

async / await函数

async/await 的调用链......等待可等待的还是返回可等待的?

ES6 -async ,await

异步/等待和任务

[ECMAScript] 说说你对async/await的理解?