题目1
console.log(1);
setTimeout(() => {
console.log(2);
}, 1000);
console.log(3);
// 运行结果1 3 2
分析:
这个运行结果很正常,因为2的输出要延迟一秒,所以3会先输出。
题目2
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
// 运行结果1 3 2
困惑:
这里我们设定了2的等待时间是0秒,0秒就是不需要等待。
那输出应该是1 2 3呀,为什么实际却还是1 3 2 ?
分析:
这里就引出的js运行机制的两个概念,单线程和任务队列。
JS的运行是单线程的,所谓单线程,意思是说js在一个同一时间只能做一件事情。
所谓任务队列,就是JS的执行任务,其中包含同步任务和异步任务。
在本题中,console.log(1);
和console.log(3);
都属于同步任务,setTimeout
属于异步任务。
首先,同步任务console.log(1);
会直接执行。
然后,遇到下一步的异步任务setTimeout
,JS会将此任务挂起(先不执行),直接往下走。
再次,同步任务console.log(3);
会直接执行。
最后,所有的同步任务都处理之后,再去响应异步任务。
所以,虽然setTimeout的时间是0,但是因为它是一个异步任务,所以是最后执行的。
导致最终的运行结果是1 3 2。
知识点:
同步任务的优先级高于异步任务
题目3
console.log(1);
while(true){
}
console.log(2);
// 运行结果1
分析:
while
是一个同步任务,所以会按顺序先while
,在执行console.log(2);
。
在while
没有执行完之前,console.log(2);
是不会执行的。
题目4
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
while(true){
}
// 运行结果1
分析:
setTimeout是一个异步任务,在所有通过任务没有执行完之前,是不会执行的。
所以,虽然在物理上,setTimeout
写在while
之前,但是2也是无法输出的。
题目5
for(var i=0;i<4;i++){
setTimeout(() => {
console.log(i)
}, 1000);
}
// 运行结果(4)4
困惑:
一般理解应该是0 1 2 3才对,怎么是4个4呢?
分析:
- 每次执行for循环的时候,由于循环体里面是一个异步任务,所有JS不会去执行它。
- for循环接着往下执行,直到循环结束,这时候i的值已经是4了。这个过程非常快,大概1毫秒都不到。
- 同步任务(也就是4个for循环体)执行完之后,会去执行异步任务。但是这个时候异步队列中是没有任务的。所以什么也不会做。
- 浏览器中有个时间模块,当这个时间模块检测到时间(这里就是1秒)到了,才会把异步放入异步队列中。所以1秒之后,这些异步任务开始执行。而输出的结果都是4,因为在第2步的时候,i已经变成4了。
知识点:
异步任务的放入时间:setTimeout中的时间到了才会放入。
异步任务的执行时间:所有同步任务执行完之后才会执行。
总结1:事件循环(Event Loop)
以下这张图可以解释js的运行机制,包括事件循环(Event Loop)
- 执行栈执行的是同步任务
- 异步任务的放入时间:setTimeout中的时间到了才会放入。
- 异步任务的执行时间:所有同步任务执行完之后才会执行。
- 开启异步任务的情况:
定时任务:setTimeout和setInterval
网络请求:ajax请求,动态<img>加载
事件绑定(DOM事件)
ES6中的Promise
总结2:同步与异步的区别
- 同步会阻塞代码的执行
console.log(100); // 第一步:打印100
alert(200); // 第二步:弹框显示200,等待用户点击确定按钮(alert是同步处理)
console.log(300) // 第三步:用户点击上一步的确认按钮之后,显示300
- 异步不会阻塞代码的执行
console.log(100); // 第一步:打印100
setTimeout(() => {
console.log(200); // 第三步:1秒之后打印200
}, 1000);
console.log(300) // 第二步:打印300