动态加载js如何保证顺序执行?

我们知道,可以使用创建script标签的方法来动态加载js文件。

但随之带来问题,如果创建多个script标签来加载多个js文件,这些文件是异步并行加载的,默认添加了async属性,最后执行顺序不能保证是当初的scrpit标签创建顺序。

也就是说,如果我们引入的js之间有依赖关系,能不能加载成功不报错 就是个随机事件了~

function createScript (src) {
    let script = document.createElement('script');
    script.src = src;
    document.body.appendChild(script);
};

createScript('a.js');
createScript('b.js'); 
// 假定b.js依赖a.js的文件内容,因为网络等原因可能b.js会在a.js之前加载完,导致报错

因此上面的代码需要改进一下,通过监听onload 和 onreadystatechange,确保a.js全部执行完成后,才能加载b.js。

参考http://www.w3help.org/zh-cn/causes/BX9013

// 方法一:
function createScript(url, success) {
  let script = document.createElement('script');
  script.src = url;
  success = success || function(){};
  script.onload = script.onreadystatechange = function() {
    // onreadystatechange和readyState针对ie
    if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
      success();
      this.onload = this.onreadystatechange = null;
      this.parentNode.removeChild(this);
    }
  }
  document.body.appendChild(script);
}
//顺序执行加载外部 JS 文件
createScript('a.js',function (){
   createScript('b.js',function (){
      alert('ok');
   });
});

到这里就实现了动态加载js也能按顺序执行的效果。

然而这个解决方法实在是太长,所以找到了另一种简单粗暴的方法。

一开始script标签中的async属性不管是设置为true or false,都会在下载该js脚本的同时,不阻塞页面其他操作,且下载完会立即执行。

在Kyle Simpson的提议下,动态插入的script标签默认设置了属性async=true,以期异步执行。同时也可以设置async=false来保持插入顺序执行。(并行加载,顺序执行)

The proposal basically preserves Webkit (and IE)'s current default behavior, but in a more formalized and flexible way, which is that any script inserted dynamically will essentially default to behaving like a parser-inserted script with `async=true`.
该提议基本上保留了Webkit(和IE)的当前默认行为,但是以一种更加形式化和灵活的方式,即,动态插入的任何脚本本质上都将默认表现为具有async = true的解析器插入脚本。

The requested extension is that a dynamically inserted script tag which has `async=false` set on it will go into a separate loading "queue" of sorts, in that all such scripts with `async=false` will load in parallel but will execute in insertion order.
要求的扩展是动态插入的脚本标签上设置了async = false,它将进入单独的各种加载“队列”,因为所有带有async = false的脚本都将并行加载但将执行按照插入顺序。

所以我们只需要加上 "async=false" :

// 方法二:
function createScript (src) {
    let script = document.createElement('script');
    script.src = src;
    // 保证JS顺序执行!
    script.async = false
    document.body.appendChild(script);
};

createScript('a.js');
createScript('b.js'); 

       一行代码解决问题,完美😼

       这里延伸一下,外部引入脚本的script标签可以有defer和async属性,讲道理他们应该是这样的:

  1. defer属性:并行加载,文档全部解析完成后(DOMContentLoaded 事件触发之前),顺序执行。<script defer src="index.js"></script>
  2. async属性:并行加载,加载完立即执行,无序。<script async src="index.js"></script>
  3. 如果都没有,就会阻塞进程,加载执行完再继续往下解析文档。<script src="index.js"></script>

       所以理论上,如果在createScript里加入script.defer也可以保证顺序执行。但实际上试了却发现顺序也不能确定。在JavaScript高级程序设计(第3版)中有这么一句:

在现实当中,延迟脚本不一定按顺序执行,也不一定会在DOMContentLoaded 事件触发前执行,因此最好只包含一个延迟脚本。

 所以还是“async = false”大法好👌

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值