DOMContentLoaded事件中使用异步
概述
我在之前的博文(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事件中使用异步的更多相关文章
- load/domContentLoaded事件、异步/延迟Js 与DOM解析
一.DOMContentLoaded 与 load事件 关于load和DOMContentLoaded事件,mdn对于它们是这样描述的: DOMContentLoaded mdn文档地址:https: ...
- JS中的异步以及事件轮询机制
一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...
- echarts异步数据加载(在下拉框选择事件中异步更新数据)
接触echarts 大半年了,从不会到熟练也做过不少的图表,隔了一段时间没使用这玩意,好多东西真心容易忘了.在接触echarts这期间也没有总结什么东西,今天我就来总结一下如何在echart中异步加载 ...
- C#中的异步调用及异步设计模式(三)——基于事件的异步模式
四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: · ...
- python:Asyncio模块处理“事件循环”中的异步进程和并发执行任务
python模块Asynico提供了管理事件.携程.任务和线程的功能已经编写并发代码的同步原语. 组成模块: 事件循,Asyncio 每个进程都有一个事件循环. 协程,子例程概念的泛化,可以暂停任务, ...
- ABP在领域事件中异步调用方法抛异常
在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...
- 深入理解Javascript封装DOMContentLoaded事件
最近在写一个Javascript的框架,刚把DOMContentLoaded事件封装好,略带小兴奋,把开发过程中遇到的原理和兼容性问题做篇笔记,省的忘记到处找. 我们在写js代码的时候,一般都会添加w ...
- NodeJS中的异步I/O、事件驱动
nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...
- DOMContentLoaded事件
今天查看百度空间源代码,发现多了个util.js文件,打开看看.里面里面定义了addDOMLoadEvent.这是干什么用的? 仔细查看代码,发现在Mozilla添加了DOMContentLoaded ...
随机推荐
- 利用python的requests发送http请求
>>> from requests import put, get >>> put('http://localhost:5000/todo1', data={'da ...
- Object.assign()解释整理
链接:https://blog.csdn.net/wang252949/article/details/79106160 语法 Object.assign(target, ...sources) 参数 ...
- python--第十四天总结(js)
选择器允许您对元素组或单个元素进行操作. jQuery 选择器 在前面的章节中,我们展示了一些有关如何选取 HTML 元素的实例. 关键点是学习 jQuery 选择器是如何准确地选取您希望应用效果的元 ...
- FortiGate 5.2/5.4 SSLVPN建立
1.定义源IP池 即用户通过sslvpn拨号成功后获取到的IP地址. 2.定义路由地址 即用户通过sslvpn拨号成功后允许获取到的路由表. 3.建立sslvpn portal 4.定义用户和用户组 ...
- FortiGate双链路不同运营商上网配置
1.防火墙端口配置 2.LLB配置
- 247. Strobogrammatic Number II输出所有对称数字
[抄题]: A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at u ...
- Django formset
一 什么是formset Form组件或ModelForm用于做一个表单验证而formset是用于做多个表单的验证组件,用于做批量操作 二 formset的使用方法 1 设置form信息 class ...
- thinkphp 视图(一)
视图 View <?php namespace app\index\controller; class Index{ public function index(){ return view() ...
- 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 ...
- 提交操作自动遮蔽实现之ajax
有时候,我们期望一些提交操作自动增加遮蔽效果,提交成功时自动去除遮蔽. 方案: 1. 提交前增加遮蔽逻辑,成功后增加去除遮蔽逻辑,但是不够智能 2.通过ajax拦截,实现自动添加\去除遮蔽效果 aja ...