概述

我在之前的博文(Performance面板看js加载)中提到过,如果利用监听DOMContentLoaded事件的方式来加载js是不能优化加载的,不能够替代jquery中的ready方法原因是加载js的时候DOMContentLoaded事件还没有结束,自然不会发生页面渲染。

于是我去看jquery的源码,发现jquery里面用到了异步。我灵机一动,对啊,如果利用异步把监听DOMContentLoaded事件来加载的js放到任务队列进行延迟那不就行了。于是我分别用setTimeout和promise的方式来实现,竟然发生了不同的结果,真是令人惊喜。我把过程记录下来,供以后开发时参考,相信对其他人也有用。

使用setTimeout

代码如下:

//haha.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="haha.css">
</head>
<body>
<div id="haha"></div>
<script type="text/javascript" src="haha.js"></script>
</body>
</html> //haha.css
div {
width: 800px;
height: 800px;
background-color: green;
font-size: 300px;
} //haha.js
document.addEventListener("DOMContentLoaded", function(event) {
setTimeout(function() {
var a = 1;
while(a < 1000000000) {
a++;
}
}, 0);
});

通过js查看首屏渲染时间,发现非常完美的得到了优化,而且效果和jquery里面的ready方法达到的效果差不多,nice。

有一点需要提出的是,setTimeout可以只加一个参数,因为第二个参数delay有一个默认值,是0。所以上面的haha.js里面的代码可以改写如下:

document.addEventListener("DOMContentLoaded", function(event) {
setTimeout(function() {
var a = 1;
while(a < 1000000000) {
a++;
}
});
});

如果把执行代码变成一个模块,也能完美的嵌入到上面的代码里面。

使用Promise

自然的,那使用es6里面的promise呢?代码如下:

//haha.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="haha.css">
</head>
<body>
<div id="haha"></div>
<script type="text/javascript" src="haha.js"></script>
</body>
</html> //haha.css
div {
width: 800px;
height: 800px;
background-color: green;
font-size: 300px;
} //haha.js
document.addEventListener("DOMContentLoaded", function(event) {
const haha = new Promise( (resolve, reject)=>{
resolve();
} );
haha.then(function() {
var a = 1;
while(a < 1000000000) {
a++;
}
});
});

但是这一次,首屏时间没有得到优化

通过复习一遍事件循环和任务队列的知识,可以解释这种情况:setTimeout是放在macrotask queue,这是这是浏览器实现的任务队列,并不是es6规范的任务队列;而promise是放在microtask queue里面,这才是es6规范的任务队列,每一个macrotask可以有许多小的microtask queue,并且当这次的macrotask执行完了之后再执行它下面的microtask queue,执行完microtask queue之后再执行下一个macrotask。

所以当我们用setTimeout的时候,会在DOMContentLoaded事件,渲染事件之后再加一个延迟事件,它是macrotask,与DOMContentLoaded事件和渲染事件同级,所以等DOMContentLoaded事件,渲染事件执行完之后才执行这个延迟的js。但是如果我们用promise的话,只会在DOMContentLoaded事件下的microtask queue塞入js,执行DOMContentLoaded事件完以后不会执行渲染事件,而是会先执行microtask queue。并且,通过看performance面板也可以看到,执行promise里面的代码的时候,DOMContentLoaded事件并没有结束,也是因为microtask queue是属于DOMContentLoaded事件的缘故。

其它

这个例子使我对macrotask queue和microtask queue的理解加深了许多!!!

DOMContentLoaded事件中使用异步的更多相关文章

  1. load/domContentLoaded事件、异步/延迟Js 与DOM解析

    一.DOMContentLoaded 与 load事件 关于load和DOMContentLoaded事件,mdn对于它们是这样描述的: DOMContentLoaded mdn文档地址:https: ...

  2. JS中的异步以及事件轮询机制

    一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...

  3. echarts异步数据加载(在下拉框选择事件中异步更新数据)

    接触echarts 大半年了,从不会到熟练也做过不少的图表,隔了一段时间没使用这玩意,好多东西真心容易忘了.在接触echarts这期间也没有总结什么东西,今天我就来总结一下如何在echart中异步加载 ...

  4. C#中的异步调用及异步设计模式(三)——基于事件的异步模式

    四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: ·                 ...

  5. python:Asyncio模块处理“事件循环”中的异步进程和并发执行任务

    python模块Asynico提供了管理事件.携程.任务和线程的功能已经编写并发代码的同步原语. 组成模块: 事件循,Asyncio 每个进程都有一个事件循环. 协程,子例程概念的泛化,可以暂停任务, ...

  6. ABP在领域事件中异步调用方法抛异常

    在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...

  7. 深入理解Javascript封装DOMContentLoaded事件

    最近在写一个Javascript的框架,刚把DOMContentLoaded事件封装好,略带小兴奋,把开发过程中遇到的原理和兼容性问题做篇笔记,省的忘记到处找. 我们在写js代码的时候,一般都会添加w ...

  8. NodeJS中的异步I/O、事件驱动

    nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...

  9. DOMContentLoaded事件

    今天查看百度空间源代码,发现多了个util.js文件,打开看看.里面里面定义了addDOMLoadEvent.这是干什么用的? 仔细查看代码,发现在Mozilla添加了DOMContentLoaded ...

随机推荐

  1. 利用python的requests发送http请求

    >>> from requests import put, get >>> put('http://localhost:5000/todo1', data={'da ...

  2. Object.assign()解释整理

    链接:https://blog.csdn.net/wang252949/article/details/79106160 语法 Object.assign(target, ...sources) 参数 ...

  3. python--第十四天总结(js)

    选择器允许您对元素组或单个元素进行操作. jQuery 选择器 在前面的章节中,我们展示了一些有关如何选取 HTML 元素的实例. 关键点是学习 jQuery 选择器是如何准确地选取您希望应用效果的元 ...

  4. FortiGate 5.2/5.4 SSLVPN建立

    1.定义源IP池 即用户通过sslvpn拨号成功后获取到的IP地址. 2.定义路由地址 即用户通过sslvpn拨号成功后允许获取到的路由表. 3.建立sslvpn portal 4.定义用户和用户组 ...

  5. FortiGate双链路不同运营商上网配置

    1.防火墙端口配置 2.LLB配置

  6. 247. Strobogrammatic Number II输出所有对称数字

    [抄题]: A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at u ...

  7. Django formset

    一 什么是formset Form组件或ModelForm用于做一个表单验证而formset是用于做多个表单的验证组件,用于做批量操作 二 formset的使用方法 1 设置form信息 class ...

  8. thinkphp 视图(一)

    视图 View <?php namespace app\index\controller; class Index{ public function index(){ return view() ...

  9. Difference Among Mercedes Star Diagnostic Tool MB Star C3 C4 C5 C6

    Mercedes Star Diagnostic Tool newly update to MB Star C6.There are many star diangostic tool in the ...

  10. 提交操作自动遮蔽实现之ajax

    有时候,我们期望一些提交操作自动增加遮蔽效果,提交成功时自动去除遮蔽. 方案: 1. 提交前增加遮蔽逻辑,成功后增加去除遮蔽逻辑,但是不够智能 2.通过ajax拦截,实现自动添加\去除遮蔽效果 aja ...