破解口诀:同步优先、异步靠边、回调垫底
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log('i--- ',i);
}, 1000);
}
console.log(i);
//输出
5
i--- 5
i--- 5
i--- 5
i--- 5
i--- 5
为什么是输出是这样呢?下面我给大家分析一下
1、for循环和循环外的console是同步的,所以先执行for循环,再执行外部的console.log。(同步优先)
2、for循环里面有一个setTimeout回调,他是垫底的存在,只能最后执行。(回调垫底)
分析:
for循环先执行,但是不会给setTimeout传参(回调垫底),等for循环执行完,就会给setTimeout传参,而外部的console打印出5是因为for循环执行完成了。
内部的setTimeout回调执行的时候 因为for循环已经执行完了 所以这个时候i=5 给setTimeout传参(5) 所以setTimeout内部的console.log输出的就是 5;
下面我们加一行代码仔细看一下这个输出的过程:
for (var i = 0; i < 5; ++i) {
setTimeout(function() {
console.log('s---',i);
}, 1000);
console.log('c---', i); //新加一行代码
}
console.log(i);
//输出
c--- 0
c--- 1
c--- 2
c--- 3
c--- 4
5 //console.log(i);输出的
s--- 5
s--- 5
s--- 5
s--- 5
s--- 5
这个时候面试官也许会问你,怎么才能正常输出0,1,2,3,4呢?
下面给出两个方法:
方法一:
for (let i = 0; i < 5; ++i) {
setTimeout(function() {
console.log('s--- ',i);
}, 1000);
}
console.log(i);
//输出
i is not defined
s--- 0
s--- 1
s--- 2
s--- 3
s--- 4
分析:
我们来分析一下,用了let作为变量i的定义之后,
for循环每执行一次,都会先给setTimeout传参,
准确的说是给loop传参,loop形成了一个闭包,
这样就执行了5个loop,每个loop传的参数分别是0,1,2,3,4
然后loop里面的setTimeout会进入消息队列排队等候。
当外部的console执行完毕,因为for循环里的i变成了
一个新的变量 _i ,所以在外部的console.log(i)是不存在的。
方法二:
var loop = function (_i) {
setTimeout(function() {
console.log('2:', _i);
}, 1000);
};
for (var _i = 0; _i < 5; _i++) {
loop(_i);
}
console.log(i);
上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步、异步、回调的例子。
let a = new Promise(
function(resolve, reject) {
console.log(1)
setTimeout(() => console.log(2), 0)
console.log(3)
console.log(4)
resolve(true)
}
)
a.then(v => {
console.log(8)
})
let b = new Promise(
function() {
console.log(5)
setTimeout(() => console.log(6), 0)
}
)
console.log(7)
口诀最重要 回忆一下(同步-异步-回调)
下面我们来分析一下:
1.看同步代码:a变量是一个Promise,我们知道Promise是异步的,
是指他的then()和catch()方法,Promise本身还是同步的,
所以这里先执行a变量内部的Promise同步代码。(同步优先)
2、Promise内部有4个console,第二个是一个setTimeout回调
(回调垫底)。所以这里先输出1,3,4回调的方法丢到消息队列中
排队等着。
3、接着执行resolve(true),进入then(),then是异步,
下面还有同步没执行完呢,所以then也去消息队列排队等候
(异步靠边)
4、b变量也是一个Promise,和a一样,执行内部的同步代码,
输出5,setTimeout滚去消息队列排队等候。
5、最下面同步输出7。
6、同步的代码执行完了,JavaScript就跑去消息队列呼叫异步的
代码:异步,出来执行了。这里只有一个异步then,所以输出8。
7、异步也over,轮到回调的孩子们:回调,出来执行了。这里有
2个回调在排队,他们的时间都设置为0,所以不受时间影响,
只跟排队先后顺序有关。则先输出a里面的回调2,最后输出b里面
的回调6。(这里 如果时间不一样的话 执行顺序就要根据时间先
后来输出)
8、最终输出结果就是:1、3、4、5、7、8、2、6。