window.onload事件偶发失效:是否与async函数或事件使用有关?
异步数据加载与页面渲染顺序问题分析
问题描述
编写了如下用于onload事件的代码,意图从data.json获取数据后,将数据填充到页面对应section的h2和p元素中:
let statElements = []; let data = []; const dailyBtn = document.getElementById("daily"); const weeklyBtn = document.getElementById("weekly"); const monthlyBtn = document.getElementById("monthly"); async function fetchDataAndProcess() { try { const response = await fetch("data.json"); data = await response.json(); const sections = document.querySelectorAll("section.card"); sections.forEach((section, index) => { const title = data[index].title.toLowerCase(); const h2Element = section.querySelector(`h2.${title}-stats`); const pElement = section.querySelector(`p.${title}-stats`); statElements.push({ h2: h2Element, p: pElement }); }); } catch (error) { console.error("Error fetching or processing data:", error); } } fetchDataAndProcess(); function weekly() { statElements.forEach((element, index) => { element.h2.textContent = `${data[index].timeframes.weekly.current}hrs`; element.p.textContent = `Last Week - ${data[index].timeframes.weekly.previous}hrs`; }); weeklyBtn.style.color = "white"; monthlyBtn.style.color = "hsl(235, 45%, 61%)"; dailyBtn.style.color = "hsl(235, 45%, 61%)"; } window.onload = weekly;
但该功能偶发失效,页面的h2和p元素内容未被填充。尝试将事件更换为DOMContentLoaded、添加load事件监听器,问题仍未解决,想咨询是否是async函数或事件使用方式导致的问题,以及是否有其他解决方案。
原因分析
核心问题是异步操作的执行顺序不确定:
fetchDataAndProcess()是异步函数,调用后会立即返回,后续的数据请求和DOM元素收集逻辑需要等待网络请求完成才会执行。window.onload(或DOMContentLoaded)触发时,fetchDataAndProcess可能还没完成数据加载和statElements的填充,此时weekly()执行时,statElements或data仍是空数组,导致无法更新页面元素。
这种偶发失效源于网络请求速度的不确定性:如果请求快于页面加载完成,功能正常;如果请求慢于页面加载,就会出现数据未填充的情况。
解决方案
方案1:确保数据加载完成后再调用渲染函数
修改逻辑,让渲染函数在fetchDataAndProcess完成后再执行,同时保留按钮的点击触发功能:
let statElements = []; let data = []; const dailyBtn = document.getElementById("daily"); const weeklyBtn = document.getElementById("weekly"); const monthlyBtn = document.getElementById("monthly"); async function fetchDataAndProcess() { try { const response = await fetch("data.json"); data = await response.json(); const sections = document.querySelectorAll("section.card"); sections.forEach((section, index) => { const title = data[index].title.toLowerCase(); const h2Element = section.querySelector(`h2.${title}-stats`); const pElement = section.querySelector(`p.${title}-stats`); statElements.push({ h2: h2Element, p: pElement }); }); // 数据加载完成后执行默认渲染(weekly) weekly(); } catch (error) { console.error("Error fetching or processing data:", error); } } function weekly() { // 先判断数据是否已加载,避免报错 if (!data.length || !statElements.length) return; statElements.forEach((element, index) => { element.h2.textContent = `${data[index].timeframes.weekly.current}hrs`; element.p.textContent = `Last Week - ${data[index].timeframes.weekly.previous}hrs`; }); weeklyBtn.style.color = "white"; monthlyBtn.style.color = "hsl(235, 45%, 61%)"; dailyBtn.style.color = "hsl(235, 45%, 61%)"; } // 绑定按钮点击事件 weeklyBtn.addEventListener("click", weekly); // 其他按钮的daily、monthly函数同理绑定 // 启动数据加载 fetchDataAndProcess();
方案2:使用Promise链式调用(兼容旧环境)
如果需要兼容不支持async/await的环境,可以用Promise的then方法:
let statElements = []; let data = []; const dailyBtn = document.getElementById("daily"); const weeklyBtn = document.getElementById("weekly"); const monthlyBtn = document.getElementById("monthly"); function fetchDataAndProcess() { return fetch("data.json") .then(response => response.json()) .then(jsonData => { data = jsonData; const sections = document.querySelectorAll("section.card"); sections.forEach((section, index) => { const title = data[index].title.toLowerCase(); const h2Element = section.querySelector(`h2.${title}-stats`); const pElement = section.querySelector(`p.${title}-stats`); statElements.push({ h2: h2Element, p: pElement }); }); weekly(); }) .catch(error => console.error("Error fetching or processing data:", error)); } function weekly() { if (!data.length || !statElements.length) return; statElements.forEach((element, index) => { element.h2.textContent = `${data[index].timeframes.weekly.current}hrs`; element.p.textContent = `Last Week - ${data[index].timeframes.weekly.previous}hrs`; }); weeklyBtn.style.color = "white"; monthlyBtn.style.color = "hsl(235, 45%, 61%)"; dailyBtn.style.color = "hsl(235, 45%, 61%)"; } weeklyBtn.addEventListener("click", weekly); fetchDataAndProcess();
额外优化点
- 在渲染函数中增加数据存在性判断,避免因数据未加载导致的报错。
- 避免直接修改元素样式,建议通过添加/移除CSS类来管理按钮状态,比如定义
.active类控制文字颜色,代码更易维护:
然后在函数中替换为:.btn.active { color: white; } .btn { color: hsl(235, 45%, 61%); }weeklyBtn.classList.add("active"); monthlyBtn.classList.remove("active"); dailyBtn.classList.remove("active");
内容的提问来源于stack exchange,提问作者Dovud Jo'rayev
相关产品推荐
相关产品推荐

