JavaScript同步、异步、回调执行顺序之经典setTimeout面试题分析

破解口诀:同步优先、异步靠边、回调垫底

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。

你可能感兴趣的:(JavaScript同步、异步、回调执行顺序之经典setTimeout面试题分析)