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

回调函数是否总会晚于触发后的代码执行?以loadScriptAsync场景为例

问题:func_a 是否总会在 func_b() 执行完成后才运行?

场景代码

loadScriptAsync('test.js', func_a);
func_b();

loadScriptAsync 实现

function loadScriptAsync(l, cb, _importance = false, _crossOrigin = false, _async = true) {
  var d = document, s = 'script', w = window;
  function go(){
    var js = d.createElement(s);
    js.onload = cb;
    if (_async)
      js.async = "true";
    if (_importance)
      js.fetchpriority = _importance;
    if (_crossOrigin)
      js.crossOrigin = 'anonymous';
    js.src = l;
    try{d.head.appendChild(js)}catch(e){
      document.documentElement.appendChild(js);
    };
  }

  if (w.addEventListener) {
    w.addEventListener("load", go, false);
  } else if (w.attachEvent) {
    w.attachEvent("onload", go); 
  } else { go() }
};

问题描述

loadScriptAsync 的作用是向 <head> 中插入 <script> 标签,并在脚本加载完成后调用传入的回调函数 func_a。请问:func_a 是否总会在 func_b() 执行完成后才运行?比如当脚本从本地主机加载极快,或者 func_b 执行耗时很长的情况,想了解背后的 JavaScript 内部运行机制。


回答

结论是:func_a 一定会在 func_b() 执行完成后才运行,不管脚本加载速度有多快,或者 func_b 执行耗时多久。核心原因要从 JavaScript 的单线程事件循环机制说起:

1. JavaScript 的核心运行规则

JavaScript 是单线程语言,所有代码都在一个执行栈里按顺序执行。只有当当前执行栈里的所有同步代码都执行完毕后,才会去处理任务队列里的异步回调(比如事件监听、网络请求完成、脚本加载完成这类回调)。

2. 结合 loadScriptAsync 的两种执行路径分析

路径一:浏览器支持 addEventListener/attachEvent

这种情况下,loadScriptAsync 会把 go 函数注册为 window.load 事件的回调。window.load 事件要等页面所有资源(包括图片、样式表、脚本等)加载完成,并且当前所有同步代码执行完毕后才会触发。

流程是:

  • 先执行 loadScriptAsync 函数,注册 window.load 回调;
  • 紧接着同步执行 func_b(),直到它完全执行完毕;
  • 等页面所有资源加载完成,才会触发 window.load,执行 go 函数插入脚本;
  • 脚本加载完成后,func_a 作为异步回调被放入任务队列,等执行栈为空时才会执行。

显然,func_a 不可能在 func_b 之前运行。

路径二:浏览器不支持事件监听,直接执行 go 函数

这种情况下,go 函数会立即执行,插入带有 async="true" 属性的脚本标签。async 脚本的特点是:

  • 加载过程是异步的,不会阻塞当前同步代码;
  • 加载完成后,不会立即执行回调,而是把回调放入任务队列,必须等当前执行栈里的所有同步代码(包括 func_b())执行完,才会被取出执行。

哪怕脚本从本地主机瞬间加载完成,它的 onload 回调 func_a 也会被挂起,直到 func_b() 执行完毕、当前执行栈为空时才会运行。

3. 极端场景验证

  • 脚本加载极快:哪怕脚本在 func_b() 执行过程中就加载完成,func_a 作为异步回调也只能进入任务队列排队,等 func_b() 执行完、执行栈清空后才会触发。
  • func_b 执行耗时很长:func_b() 会占用执行栈直到完成,期间所有异步回调(包括 func_a)都无法执行,必须等 func_b() 执行结束才行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:23:11