概述

我在之前的博文(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. 2.Yum仓库优化

    1.[初始yum源备份]-[yum更换为国内源]-[添加epel扩展源] #!/bin/bash mkdir /etc/yum.repos.d/backup/ mv /etc/yum.repos.d/ ...

  2. .do的消除

    其实就是在web.xml中去掉.do即可  那里有拦截器作用,什么样的文件可以进入前端控制器1

  3. (转)PWA(Progressive Web App)渐进式Web应用程序

    PWA 编辑 讨论 PWA(Progressive Web App)是一种理念,使用多种技术来增强web app的功能,可以让网站的体验变得更好,能够模拟一些原生功能,比如通知推送.在移动端利用标准化 ...

  4. NYOJ737石子合并(二)-(区间dp)

    题目描述:     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出 ...

  5. C++ 11 创建和使用 unique_ptr

    unique_ptr 不共享它的指针.它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法.只能移动unique_ptr.这意味着,内存资 ...

  6. 十一 hashlib模块

    # 1.什么叫hash:hash是一种算法(3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法),该算法接受传入的 ...

  7. PHP Laravel定时任务Schedule

    前提:本文方法是利用Linux的crontab定时任务来协助实现Laravel调度(Mac也一样). 一.首先添加Crontab定时任务,这里只做简单介绍. 用命令crontab -e 添加如下内容 ...

  8. POI2014解题报告

    穷酸博主没有bz权限号, 也不会去$poi$官网, 在某咕刷的$poi$,按照某咕的难度排序刷, poi~ $Luogu3572 PTA-Little Bird$ 单调队列, 队列内按照 步数为第一关 ...

  9. Docker架构

    Docker使用客户端-服务器(C/S)架构模式,使用远程API来管理和创建Docker容器. Docker容器通过Docker镜像来创建. 容器与镜像的关系类似于面向对象编程中的对象和类. Dock ...

  10. linux学习第十二天 (Linux就该这么学)找到一本不错的Linux电子书,附《Linux就该这么学》章节目录

    本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极其适合用于Linux技术入门教程或讲课辅助教材,目前是国内最值得去读的Linux教材,也是最有价值 ...