JS脚本未按顺序下载和评估。

7

我在<body>中的最后三个元素中有3个<script>标记。 它们会加载外部的.js脚本:

    ...
  </div>
  <script src="https://somewhere.com/scripts/script1.js"></script>
  <script src="https://somewhere.com/scripts/script2.js"></script>
  <script src="https://somewhere.com/scripts/script3.js"></script>
</body>
</html>

所有脚本只包含一行代码:console.log(启动脚本 X),其中X为脚本编号。 我期望脚本被顺序执行,输出结果如下:
Start script 1
Start script 2
Start script 3

但输出似乎是随机的。更准确地说,它看起来像评估顺序仅取决于脚本下载完成的顺序。

我是否正确期望评估顺序模仿它们在 HTML 中被引用的顺序? 如果不是,除了将它们全部合并到一个 .js 文件中,我该如何确保它们按正确顺序进行评估?

2个回答

5
您的观察是正确的。我理解您的期望,但这种评估顺序意味着文件要么按顺序而非并行方式下载,要么按顺序而非并行方式进行评估。
让它们按顺序下载或执行会很麻烦。如果一个脚本永远不下载,后续脚本应该永远挂起吗?那将比当前情况更糟糕。
如果脚本之间没有依赖关系怎么办?即使没有必要等待对方,它们也应该互相等待吗?当然不应该。
因此,实际行为是正确的,但您说的也确实意味着存在一些问题。
通常,解决此问题的方法是将脚本分解为类和函数,以便在理想情况下执行,而不是在脚本下载时执行,并在body标签中添加onload事件或通过AddEventListener创建load事件。
假设script1中有f1函数,script2中有f2函数,...,那么您可以这样做:
window.addEventListener('load', (event) => {
  f1();
  f2();
  //...
});

你甚至可以这样做:

function load() {
    f1();
    f2();
    //...
}

然后将其添加到body中:

<body onload="load()">
    <!-- ... -->
</body>

3

选项1:defer

这篇文章解释了 defer 和 async 脚本标签属性。被标记为 defer 的脚本会在解析完成后按照它们在标记中定义的顺序执行。

 <script src="https://somewhere.com/scripts/script1.js" defer></script>
 <script src="https://somewhere.com/scripts/script2.js" defer></script>
 <script src="https://somewhere.com/scripts/script3.js" defer></script>
 <script src="https://somewhere.com/scripts/script4.js" defer></script>

选项二:托管的顺序加载

该脚本应逐个按顺序加载文件,您所需的顺序应得到保留。

const scripts = [
  "https://somewhere.com/scripts/script1.js",
  "https://somewhere.com/scripts/script2.js",
  "https://somewhere.com/scripts/script3.js",
  "https://somewhere.com/scripts/script4.js"
];

let sctr = 0;
const doScript = () => {
  if (sctr++ >= scripts.length) {
    console.log('all scripts loaded syncronously');
    return;
  }
  const scriptPromise = new Promise((resolve, reject) => {
    const script = document.createElement('script');
    document.body.appendChild(script);
    script.onload = resolve;
    script.onerror = reject;
    script.async = true; // they're loading one at a time, so async is ok
    script.src = scripts[sctr];
  });

  scriptPromise.then(() => {
      console.log(scripts[sctr], 'loaded');
      doScript();
    },
    (err) => {
      console.error(scripts[sctr], 'failed', err);
      // halting
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接