从浅入深的解决setTimeout,Promise,Promise.then的先后执行顺序。
如果你已经对for循环,作用域,闭包等很熟练了,那么请直接点击**这里“,进入到重点**中。
让我们从一个最简单的for循环入手
for (var i = 1;i <= 5;i ++) {
console.log(i)
}
//1,2,3,4,5
如果我们修改一下需求,要求每秒输出一次呢?(老生常谈)
for (var i = 1;i <= 5;i ++) {
setTimeout(function() {
console.log(i)
},i*1000)
}
//6,6,6,6,6
没错,是每隔一秒输出一次6,一共五次6(不是5呀老哥,虽然之前我也认为是5)
那么,怎么实现每隔一秒输出一次相对应的数字呢?
1、ES 6 let
for(let i = 0;i<5;i++) {
setTimeout(function timer(){
console.log(i);
}, i * 1000);
}
2、自调函数
for(let i = 0;i<5;i++) {
+function(i){
setTimeout(function timer() {
console.log(i)
}, i * 1000);
}(i);
}
3、setTimeout的扩展参数(第三个参数)
for (var i=1; i<=5; i++) {
setTimeout( function timer(i) {
console.log(i);
}, i*1000,i );
}
然后再来看Promise
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('then1')
})
//promise1,then1
执行Promise后,打印promise1,然后走了.then函数,打印then1(so easy ————二哈哥)
好,现在稍微复杂一点点(重点)。
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('then1')
})
for (var i = 1;i <= 5;i ++) {
setTimeout(function() {
console.log(i)
},i*1000)
console.log(i)
}
//promise1,1,2,3,4,5
//then1
//6,6,6,6,6
为什么打印的注释要分三行呢?
因为进行了三次队列任务:
队列一
执行代码–>进入任务队列–>进入Promise函数–>打印promise1–>将Promise.then()加入到Promise.then队列中等待执行–>进入for循环–>将setTimeout加入到setTimeout队列中等待执行–>打印1,2,3,4,5
到此时,已经打印出了promise1,1,2,3,4,5,then1 。现在有两个队列,先选择哪一个呢? 因为for循环中的定时器要在1000ms后执行,所以先执行Promise.then()函数
队列二
执行Promise.then()函数–>输出then1
目前控制台输出promise1,1,2,3,4,5,then1
队列三
一秒后执行一次console.log(i),共执行5次(请注意,此时i已经循环过五次了,此次执行,其数值为6!!!)
打印 6,6,6,6,6
输出完毕,结果为:promise1,1,2,3,4,5,then1,6,6,6,6,6
现在,我们来整理一下,为什么先执行for循环下面的1,2,3,4,5而不是先执行.then函数呢?
因为其涉及到任务队列
任务队列可分为宏任务和微任务。XHR回调、事件回调(鼠标键盘事件)、setImmediate、setTimeout、setInterval、indexedDB数据库操作等I/O都属于宏任务,process.nextTick、Promise.then、MutationObserver(html5新特性)属于微任务。
任务的执行顺序:先按照程序顺序从上往下执行(当前的整体代码我们可以认为是宏任务),遇见宏任务则把他添加到相应的任务队列中,直到第一轮代码执行完;然后再去执行对应的微任务。
例如上述栗子:第一次执行完宏任务输出 promise1,1,2,3,4,5,然后执行微任务输出then1,然后再执行第二轮的宏任务,第二轮的微任务……
累,不知道说清楚了没有,如果有什么问题,一定要及时提出来,哪怕一个错字。
到此栗子基本结束了,但是如果setTimeout不延迟(0ms)或者其内部包含一个new Promise呢?
那么,重点来了
我这边文章是根据大佬的文章简化了,下面附上大佬文章的连接。
大佬直通车?