先观为敬
//执行以下JavaScript代码,分析代码执行顺序
console.log('javascript1')
setTimeout(() => {
console.log('setTimeout1');
Promise.resolve().then(() => {
console.log('promise1');
})
}, 0);
setTimeout(() => {
console.log('setTimeout2');
Promise.resolve().then(() => {
console.log('promise3');
})
setTimeout(() => {
console.log('setTimeout3');
}, 0)
}, 0)
Promise.resolve()
.then(() => {
console.log('promise4');
}).then(() => {
console.log('promise2');
})
console.log('javascript2');
讲真的,一开始我是懵逼的,反正正确的打印顺序肯定不是从上到下执行的。
js浏览器的执行机制——Event-Loop
这里为什么会强调说是浏览器,我们都知道,在node平台的JavaScript执行机制和浏览器端的执行是有区别的,至于何种区别,不在本文的讨论之中
我们知道js语言是单线程的,这是js语言设计的初衷。起初,作为一个为web开发的语言,如果设计为多线程的,那么就会存在一个矛盾。比如:线程A对一个DOM结构删除了,线程B正在对这个DOM修改,那么浏览器是不是就很蛋疼了。但是在浏览器执行时,又不能按照顺序执行,如果一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如我们想打开一个网页,但是该网页中包含的大量图片而且加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?可是问题总归是要解决的,于是就诞生了:
- 同步执行
- 异步执行
下面介绍两个例子:
//同步执行
console.log('step1')
console.log('step2')
console.log('step3')
/*
*打印顺序为
* step1
* step2
* step3
*/
//异步执行
console.log('step1')
setTimeout(function(){
console.log('step2')
},0)
console.log('step3')
/*
*打印顺序为
* step1
* step3
* step2
*/
JavaScript执行机制Event-Loop如下图所示
我们尝试通过JavaScript执行机制去分析代码执行顺序。首先先介绍两个概念
- 宏任务:包括整体代码script,setTimeout,setInterval,setImmediate
- 微任务:Promise,process.nextTick
分析上面的异步例子
console.log('step1')
作为第一级宏任务,需要立即执行
setTimeout(function(){ console.log('step2') },0)
作为第二级宏任务,需要等到第一级宏任务执行完成后,再执行
console.log('step3')
作为第一级宏任务,需要立即执行
再尝试分析一串简单的代码
setTimeout(function(){
console.log('step1')
});
new Promise(function(resolve){
console.log('step2');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('step3')
});
console.log('step4');
1.setTimeout(function(){ console.log('step1') });
作为异步任务放在宏任务队列中,等待执行
2.new Promise
立即执行,打印step2
,then链是微任务放入微任务队列中等待本次宏任务执行完成后执行
3.console.log('step4')
立即执行,打印step4
4.本次宏任务执行完成,查看微任务队列中发现还有promise的then链没有执行,立即执行then链中的console.log('step3')
5.本轮任务完成后,查看宏任务的队列里还有一个定时器,执行定时器的console.log('step1')
所以打印结果为step2-step4-step3-step1
当理解了js浏览器的执行机制后,再来看文章开始的例子就可以很清晰的得出代码打印的结果
javascript1
javascript2
promise4
promise2
setTimeout1
promise1
setTimeout2
promise3
setTimeout3
setTimeout的执行
setTimeout(function(){
console.log('setTimeout')
},1000);
通常setTimeout
定时器是指在xx秒后执行,其实在js的运行机制里,是在xx秒后将setTimeout
里的回调函数推入到event queue队列中,但是并没有立即执行,而是等到js主线程空闲之后再执行,比如上面的代码中,一秒钟后执行console.log('setTimeout')
,如果主线程的任务在1秒钟内没有执行完成,那么console.log('setTimeout')
也不会在一秒后执行。所以说setTimeout
在一秒钟后并且主线程空闲两个条件同时满足才会执行。