前言:本篇带你了解JavaScript代码的执行顺序是怎么样的,在编写JS代码的时候如果你经常顾虑编写的某一行代码是否会提前执行或者不执行,总之就是不按你的逻辑执行这行代码,那么你就应该去了解一下JS代码的执行顺序以及影响JS代码的执行顺序的情况,像声明函数变量或者函数时会提前执行初始化等等知识点。
一、了解JS
首先我们需要了解,js是单线程,基于事件循环,非阻塞IO的,如果你对这些词汇比较陌生的话就需要去百度理解了。
特点: 处理I/O型的应用,不适合CPU运算密集型的应用。
事件循环中使用一个事件队列,在每个时间点上,系统只会处理一个事件,即使电脑有多个CPU核心,也无法同时并行的处理多个事件。因此,node.js在I/O型的应用中,给每一个输入输出定义一个回调函数,node.js会自动将其加入到事件轮询的处理队列里,当I/O操作完成后,这个回调函数会被触发,系统会继续处理其他的请求。
二、我们从一个简单的例子理解
function test(){
console.log('start');
setTimeout(()=>{
console.log('setTimeout');
},0);
console.log('end');
}
test();
// 打印结果: start、end、setTimeout
JS是单线程的,会从上到下依次执行代码,但是当遇到异步操作的时候就会打乱代码的执行顺序。
异步函数包括:seTimeout,setInterval,dom事件,ajax,Promise,process.nextTick等函数 。
这里先大致介绍一下执行流程:首先代码会自上而下执行,当遇到非异步代码就会直接执行,当遇到包含有异步操作的代码,浏览器会将其放入到任务队列中,然后继续执行下面的代码,当整个代码扫描完毕后,就会去执行任务队列中的任务,然后重复最开始的操作,直至任务队列中为空。任务队列中分为宏任务和微任务,微任务会优先执行,下面会详细介绍。
所以上面的代码执行顺序就可以简单的理解了,代码运行函数后首先是打印"start",人后继续向后执行,遇到setTimeout异步操作,将其放到任务队列中,人后继续向后执行打印“end”,第一轮代码执行完毕,去执行任务队列中的代码,打印“setTimeout”,发现任务队列为空,至此程序执行完毕。
三、JS代码的执行流程图
这个流程图看起了可能会有点难以理解,因为它是一个递归执行的过程,而且判断什么是微任务,什么事宏任务也不是很了解,下面我们对流程图做进一步分析。
- 执行全局Script同步代码,同步任务直接执行,遇到异步任务添加到任务队列(有微任务队列和宏任务队列之分)。
- 全局Script代码执行完毕后,
执行栈
Stack会清空; - 循环执行
微队列
的任务,直到直到把微任务
执行完毕。(注意:如果在执行微任务
的过程中,又产生了微任务
,那么会加入到微队列
的末尾,也会在这个周期被调用执行,宏任务同理。) 微队列
中的所有微任务
都执行完毕,此时循环执行宏队列
中的任务。- 重复第3、4两个步骤,直至任务队列为空。
1、什么是宏任务?
- I/O,事件队列中的每一个事件都是一个macrotask
- setTimeout / setInterval
- MessageChannel是通信渠道API,ie11以上和其它浏览器支持。
- setImmediate 目前只有IE10以上实现了该方法其它浏览器不支持.作用回调功能,node支持。
- requestAnimationFrame 也算宏任务 node不支持。
2、什么是微任务
- Promise.then catch finally
- MutationObserver 浏览器支持 IE11以上 node不支持,它会在指定的DOM发生变化时被调用
- process.nextTick 浏览器不支持 node支持
3、Promise与await
(1) Promise和async中的立即执行
Promise中的异步体现在then和catch中,所以写在Promise中的代码是被当做同步任务立即执行的。而在await中,在出现await出现之前,其中的代码也是立即执行的。那么出现了await时候发生了什么呢?从字面意思上看await就是等待,await 等待的是一个表达式,这个表达式的返回值可以是一个promise对象也可以是其他值。
很多人以为await会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上await是一个让出线程的标志。await后面的表达式会先执行一遍,将await后面的代码加入到微任务队列中,然后就会跳出整个async函数来执行后面的代码。由于async await 本身就promise+generator的语法糖。所以await后面的代码也属于微任务。
四、实战
async function dog() {
console.log('async1 start');
await cat();
console.log('async1 end');
}
async function cat() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
dog();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
打印顺序:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/