概述

我在之前的博文(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函数定义和使用

    函数是一段可以重复多次调用的代码,通过输入的参数值,返回需要的结果.通过使用函数,可以提高代码的重复利用率.本文主要介绍Python函数的定义.调用和函数参数设置方法. 函数的定义 Python函数定 ...

  2. Tengine安装(阿里baba的)-Nginx

    在先前的文章中介绍过Tengine,先前只是使用了运维人员配置好的内容,未自己进行过安装配置.周末闲来无事,对于Tengine进行了尝试性的安装.记录下面方便以后再做改进. Tengine官网上有个非 ...

  3. java 导mysql数据为表格给浏览器接收

    jar 包准备 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</a ...

  4. [C++]字符串相关操作

    获取字符串长度 String str str.length(); //获取String类型字符串长度 str.size(); //STL获取容器中元素个数 Char* s strlen(s); //以 ...

  5. FortiGate日志中session clash

    1.出现于:FortiGate v5.0和v5.2 2.出现原因 Session clash messages appear in the logs when a new session is cre ...

  6. 《Java程序设计》第二周学习总结(一)

    教材学习内容总结 标识符.关键字 基本数据类型 类型转换运算 输入.输出数据 数组 教材学习中的问题和解决过程 问题1:在创建vim并编写完成后出现以下情况 问题1解决方案:打开虚拟机的文件目录,发现 ...

  7. ORACLE数据库测试数据插入速度[z]

    一,没有优化的速度:Executed in 69.436 seconds drop table t purge;create table t(x int);/*清空共享池,注意在生产环境中千万不能做这 ...

  8. 深入理解JVM(五)JVM优化策略

    5.2一些案例: 1.高性能硬件部署策略: (1)背景:某公司升级了硬件(CPU升级为4核,内存增加为16G),发现不定期出现网页失去响应. (2)原因:①内存增加之后,项目中有在内存中处理文件的大对 ...

  9. Python 多进程编程之 进程间的通信(在Pool中Queue)

    Python 多进程编程之 进程间的通信(在Pool中Queue) 1,在进程池中进程间的通信,原理与普通进程之间一样,只是引用的方法不同,python对进程池通信有专用的方法 在Manager()中 ...

  10. js中加“var”和不加“var”的区别

    JavaScript 拥有动态类型.这意味着相同的变量可用作不同的类型: var x // x 为 undefined var x = 6; // x 为数字 var x = "Bill&q ...