setTimeout与setInterval
前言:关于最近面试遇到的setTimeout与setInterval相关面试题,网上搜索相关资料,进行学习总结。
1、setTimeout与setInterval的区别?
setTimeout()方法用来指定某个函数或字符串在指定的毫秒数之后执行。它返回一个整数,表示定时器的编号,这个值可以传递给clearTimeout()用于取消这个函数的执行。
setInterval的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
setTimeout和setInterval函数,都返回一个表示计数器编号的整数值,将该整数传入clearTimeout和clearInterval函数,就可以取消对应的定时器。
2、看代码,写结果
for (var i = 0; i < 5; i++) { setTimeout(function (){ console.log(i); },1000); }
结果:5,5,5,5,5
setTimeout是异步执行的,1000毫秒后向任务队列里添加一个任务,只有主线上的全部执行完才会执行任务队列里的任务,所以当主线程for循环执行完之后i的值为5,这个时候再去任务队列中执行任务,i全部为5;每次for循环的时候setTimeout都会执行,但是里面的function不会执行,因此放了5次,1000毫秒后全部执行完任务队列中的函数,所以输出五个5.
同时,i是var定义的,不属于for循环体,属于全局global。等for循环结束,i已经等于5了,这个时候执行回调函数,里面console.log(i)的i向上找作用域,只能找到全局下的i,即5.所以输出都是5.
3、如何解决上述问题,变成0,1,2,3,4
a、立即执行函数
这样console.log(i)中的i就保存在每一次循环生成的立即执行函数中对的作用域里了。
for (var i = 0; i < 5; i++) { (function(i){ //立刻执行函数 setTimeout(function (){ console.log(i); },1000); })(i); }
b、let作为代码块作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
for (let i = 0; i < 5; i++) { //let 代替 var setTimeout(function (){ console.log(i); },1000); }
4、setTimeout的作用是将代码推迟到指定时间执行,如果指定时间为0,即setTimeout(f,0),那么会立刻执行吗?
不会。必须等当前脚本的同步任务和队列任务中所有事件执行完,才会执行setTimeout指定任务,将第二参数设置为0,目的是之前所有任务执行完后就立即执行,尽可能早得执行指定任务
5、看代码,写结果
console.log("script start"); setTimeout(function(){ console.log("setTimeout"); },0); Promise.resolve().then(function(){ console.log("promise1"); }).then(function(){ console.log("promise2"); }); console.log("script end");
结果:
script start
script end
promise1
promise2
setTimeout
setTimeout和Promise调用都是异步任务,都是通过任务队列进行管理调度。分为宏任务队列和微任务队列,可以看出Promise比setTimeout()先执行。因为Promise定义之后便会立即执行,其后的.then()是异步里面的微任务。而setTimeout()是异步的宏任务。
从script(整体代码)开始第一次循环,全局上下文进入函数调用栈(栈底),如果有可执行代码就进行正常的入栈出栈,如果有上面提到的setTimeout和Promise,就将任务分发到各自队列,直到调用栈清空(只剩全局),然后执行所有的微任务队列(Promise队列),这就是第一次循环。当所有可执行的微任务执行完毕之后,循环再次从宏任务(setTimeout队列)开始执行入栈出栈任务分发等,执行完毕,然后再执行所有的微任务,第二次循环结束。。。这样一直循环下去,直到再也没有可执行的任务。这就是JS的循环机制。
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve,reject){ console.log('打印'+3); }).then( console.log('打印'+4));; console.log('打印'+10); new Promise(function(resolve,reject){ setTimeout(function () { console.log('打印'+5); }); }).then( console.log('打印'+6)); setTimeout(function(){ new Promise(function(resolve,reject){ console.log('打印'+7); }); })
打印1
打印3
打印4
打印10
打印6
打印2
打印5
打印7
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve){ console.log('打印'+3); resolve(); }).then(function(){ console.log(4); } ); console.log('打印'+10); new Promise(function(resolve){ setTimeout(function () { console.log('打印'+5); }); resolve(); }).then(function(){ console.log('打印'+6)}); setTimeout(function(){ new Promise(function(resolve){ console.log('打印'+7); }); })
打印1
打印3
打印10
4
打印6
打印2
打印5
打印7