setTimeout基本用法
setTimeout(code,millisec)
setTimeout函数接受两个参数,第一个参数code是将要推迟执行的函数名或者一段代码,第二个参数millisec是推迟执行的毫秒数。
例如:
setTimeout('console.log(2)',100);//直接在setTimeout中直接执行代码, 需要以字符串的形式去写,引擎内部会将字符串转为可执行的代码 setTimeout(function(){console.log(2)},100);
什么是 Event Loop
Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制。
JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题。
在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")
上图主线程的绿色部分,还是表示运行时间,而橙色部分表示空闲时间。每当遇到I/O的时候,主线程就让Event Loop线程去通知相应的I/O程序,然后接着往后运行,所以不存在红色的等待时间。等到I/O程序完成操作,Event Loop线程再把结果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。
可以看到,由于多出了橙色的空闲时间,所以主线程得以运行更多的任务,这就提高了效率。这种运行方式称为"异步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode)。
执行顺序
console.log(1); //Time1 setTimeout(function(){ console.log(2); },300);for (var i = 0;i<10000;i++) { console.log(4); } //Time2 setTimeout(function(){ console.log(3); },300);
运行结果:循环中的方法会先执行,然后再执行settimeout中的方法。
for循环中的setTimeout(异步操作)
function test() { for (var i = 0; i < 5; ++i) { setTimeout(function() { console.log("index is :", i); }, 1000); } }
test();
结果会显示:
该操作几乎是在同一时间完成,setTimeout定时根本就没有起作用,这是因为:单线程的js在操作时,对于这种异步操作,会先进行一次“保存”,for循环执行结束后,i的值已经变成5,setTimetout调用均是在for循环结束后进行的,所以自然而然输出都是5。一般情况下,我们使用递归实现:
function test(i) { if (i< 5) { console.log("index is :", i); setTimeout(function() { box7(i+ 1); }, 1000) } }
test(0);
我们也可以使用ES6中 async Promise 实现,代码如下:
var asyncFunc = function(arr, i) { return new Promise(function(resolve, reject) { setTimeout(function() { arr.push(i); console.log("index is : ", i); resolve();//异步操作完成后执行resolve方法,配合.then使用,本例子中未使用到。 }, 1000); }); } var box5 = async function() { var arr = []; for (var i = 0; i < 5; i++) { await asyncFunc(arr, i);//async函数执行时,如果遇到await就会先暂停执行,等待Promise对象异步操作完成后,恢复async函数的执行并返回解析值。 } console.log(arr); } box5();
补充20200611:
解决办法一
for (var i = 0; i < 5; i++) { (function(i){ //立刻执行函数 setTimeout(function (){ console.log(i); },1000); })(i); }
这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。
解决办法二
for (let i = 0; i < 5; i++) { //let 代替 var setTimeout(function (){ console.log(i); },1000); }
let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
setTimeout在vue中应用
vue中直接使用this,此时的this指向的是window对象,虽然end方法仍然被执行了,但是没有延迟1s后执行的效果。
setTimeout(this.end(),1000);
正确的做法是方法中将this存在变量that中,此时执行setTimeout函数时,setTimeout函数内的that就会访问到这个变量,就会得到当前对象。
export default { methods: { start: function () { let that=this setTimeout(function() { that.end() }, 1000); } }
也可以使用箭头函数:
export default { methods: { start: function () { setTimeout(() => { this.end() }, 1000); } }
参考: