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

如何避免使用setTimeout?加载含HTML与JS的混合文件相关问题

问题

加载的是包含HTML和JS的混合文件,而非纯JS文件。目前只能通过setTimeout判断脚本加载完成,但尝试为创建的script元素添加load事件监听未触发,使用DOMContentLoaded事件也无效,希望找到替代setTimeout的方案。

加载的混合文件内容:

<div>HTML part</div>
<script>
  // js part
</script>

当前使用的loadVanilla函数:

loadVanilla: async function(arg, parent, callback) {
    return new Promise((resolve, reject) => {
        fetch(arg, {}).then(res => {return res.text()}).then(html => {
            let t = html.split('<script defer>')[1];
            let hc = html.split('<script defer>')[0];
            let sc = t.split('</script>')[0];
            let s = document.createElement('script');
            s.innerHTML = sc;
            if(typeof parent === 'undefined') {
                document.body.innerHTML += hc;
            } else {
                parent.innerHTML += hc;
            }
            document.body.appendChild(s);
            setTimeout(() => {
                console.log('S LOADED')
                resolve(`loaded good ${arg} \n `)
                if(callback) callback()
            }, 10)
        }).catch((err) => {
            reject('bad')
        })
    })
},

尝试无效的事件监听代码:

s.addEventListener('DOMContentLoaded', function() {
    console.log('S LOADED')
    resolve(`loaded good ${arg} \n `)
    if(callback) callback()
})
解决方案

无效原因说明

  • script元素的load事件仅对**外部脚本(通过src引入)**触发,内联脚本不会触发该事件。
  • DOMContentLoaded是整个文档的DOM加载完成事件,和单个脚本的执行完成状态无关,因此监听它无法判断目标脚本是否执行完毕。

分场景处理

场景1:目标脚本是同步执行的

内联脚本插入到DOM后会同步执行,也就是document.body.appendChild(s)执行完成时,脚本已经执行完毕。因此可以直接移除setTimeout,在append之后立即执行resolve和回调:

修改后的关键代码:

document.body.appendChild(s);
// 内联脚本已同步执行完成
console.log('S LOADED')
resolve(`loaded good ${arg} \n `)
if(callback) callback()

场景2:目标脚本包含异步逻辑

如果脚本内部有异步操作(比如fetch、setTimeout),需要让脚本主动通知加载完成:

  1. 修改混合文件的脚本部分,在异步逻辑完成后调用一个全局回调:
<div>HTML part</div>
<script>
  // js part(包含异步逻辑)
  async function init() {
    // 异步操作示例:fetch数据
    await fetch('/api/data');
    // 执行完所有逻辑后调用全局回调
    window.__scriptLoadedCallback?.();
  }
  init();
</script>
  1. 在loadVanilla函数中定义全局回调,等待脚本触发:
loadVanilla: async function(arg, parent, callback) {
    return new Promise((resolve, reject) => {
        // 定义全局回调函数
        window.__scriptLoadedCallback = () => {
            delete window.__scriptLoadedCallback; // 执行后清理全局变量
            console.log('S LOADED')
            resolve(`loaded good ${arg} \n `)
            if(callback) callback()
        };

        fetch(arg, {})
            .then(res => res.text())
            .then(html => {
                let t = html.split('<script defer>')[1];
                let hc = html.split('<script defer>')[0];
                let sc = t.split('</script>')[0];
                let s = document.createElement('script');
                s.innerHTML = sc;
                
                // 插入HTML部分
                if(typeof parent === 'undefined') {
                    document.body.innerHTML += hc;
                } else {
                    parent.innerHTML += hc;
                }
                // 插入脚本
                document.body.appendChild(s);
            })
            .catch((err) => {
                delete window.__scriptLoadedCallback; // 出错时也要清理全局变量
                reject('bad')
            })
    })
},

内容的提问来源于stack exchange,提问作者Nikola Lukic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:20:03