You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:40:11