Failure is part of learning. we should never give up the struggle in life

js script标签异步加载

传统同步加载

//同步加载
<script type="text/JavaScript" src="index.js"></script>

defer实现

添加 defer 属性就可以变成异步,但 不会 立即执行
等待DOM解析完毕才会执行
(IE8-IE4都支持)

//异步加载 会等待DOM树加载完毕再执行
<script type="text/JavaScript" src="index.js" defer="defer"></script>

async实现

与上面恰恰相反,只要该链接文件加载完毕,会立即执行
不论DOM是否解析完毕
(W3C H5属性 IE9及以上支持)
当defer和async同时出现在一起,除了IE外一律只认async(如高版本IE也是认async但低版本会认defer,firefox也是认async)
不建议异步加载写DOM操作有关的,一般都是封装的函数且与DOM操作无关

//加载完就会执行
<script type="text/JavaScript" src="index.js" async="async"></script>

生成异步script标签

<script type="text/JavaScript">
    var s = document.createElement('script');
    s.type='text/javascript';
    // s.async=true;
    s.src='index.js';
    document.body.appendChild(s);
    //这种写法虽然是异步的但是会阻塞onload方法
  </script>

//用下面的写法就可以实现onload过后再进行异步的加载
<script type="text/JavaScript">
    function async_load() {
      var s = document.createElement('script');
      s.type='text/javascript';
      // s.async=true;
      s.src='index.js';
      document.body.appendChild(s);
    }
    if (window.attachEvent) {
        window.attchEvent('onload',async_load);//(IE)
    }else{
        window.addEventListener('load',async_load,false);
    }
  </script>

//最好写成立即执行函数,成为一个独立的模块
  <script type="text/JavaScript">
    (function () {
      function async_load() {
      var s = document.createElement('script'),
      //将这个生成的script添加到最上面
      t = document.getElementsByTagName('script')[0]
      ;
      s.type='text/javascript';
      // s.async=true;//可写可不写这样生成的已经是异步的了
      s.src='index.js';
      // document.body.appendChild(s);
      t.parentNode.insertBefore(s, t);//将这个生成的script添加到最上面

    }
    if (window.attachEvent) {
        window.attchEvent('onload',async_load);
    }else{
        window.addEventListener('load',async_load,false);
    }
    })();
  </script>
//在s.src的时候已经开始下载这个对应的文件了,当动态创建的script放入HTML里时就已经执行了

案例

此时有一个 utils.js ,既要异步的去加载这个文件还要在加载完成后调用文件里的 test1test2 方法
在开头和结尾同步的调用的 utils2.js 里的 test3 来观察它们的执行顺序
写一个 utils.js(该文件和下面HTML在同级目录)

var utils ={
      test1:function () {
        console.log('test1');
      },
      test2:function () {
        console.log('test2');
      },
    }

再写一个utils2.js(也在一个目录)

(function () {
    console.log('test3');
})()

随便写一个html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <!-- 调用了test3 -->
    <script src="utils2.js"></script>

</head>
<body>
    <div class="div">1</div>
    <div class="div">2</div>
    <div class="div">3</div>
    <div class="div">4</div>
    <div class="div">5</div>
    <div class="div">6</div>
    <div class="div">7</div>
    <div class="div">8</div>
    <div class="div">9</div>
    <div class="div">10</div>
    <script>
        console.log(1);
        console.log(2);
        console.log(3);
        console.log(4);
        console.log(5);
        console.log(6);
        console.log(7);
        console.log(8);
        console.log(9);
        console.log(10);
        utils_test1_('utils.js', 'test1');//异步调用test1

        //注意这里有一个定时器
        setTimeout(function () { console.log(11); }, 0);

        utils_test1_('utils.js', 'test2');//异步调用test2
        //生成script标签函数
        function utils_test1_(url, fn) {
            var s = document.createElement('script'),
                Oscript = document.getElementsByTagName('script')[0];
            s.type = 'text/javascript';
            // s.src = url;
            if (s.readyState) {//IE
                s.onreadystatechange = function () {
                    if (s.readyState == 'loaded' || s.readyState == 'complete') {
                        utils[fn]();
                    }
                };
            } else {//非IE
                s.onload = function () {
                    utils[fn]();
                };
            }
            //url无论放在这里还是上面都可以,因为readyState、onload是一直在监听的
            // 直到url下载完成后就会执行相对应的utils[fn]();
            s.src = url;
            Oscript.parentNode.insertBefore(s, Oscript);
        }
        console.log(12);
        console.log(13);
        console.log(14);
        console.log(15);
        console.log(16);
        console.log(17);
        console.log(18);
        console.log(19);
        console.log(20);
        console.log(21);
        console.log(22);
        console.log(23);
        console.log(24);
        console.log(25);
        console.log(26);
        console.log(27);
        console.log(28);
        console.log(29);
        console.log(30);
    </script>
    <!-- 调用了test3 -->
    <script src="utils2.js"></script>
</body>
</html>

观察打印的结果就可以看到 test1test2 的打印并没有阻塞console.log按顺序打印
而且你不断的刷新后会发现 有时 test1 在前, test2 在后,有时 test2 在前, test1 在后面,取决于谁先加载完
但你可以看到 test3 是按照同步加载的,而且是 早于定时器 的(挖坑)

发表评论

电子邮件地址不会被公开。 必填项已用*标注