setTimeout()
我们先从这个函数说起,setTimeout()
- 暂停指定的毫秒数后执行指定的代码。
setTimeout(fn,ms)
;其中共有两个参数,其中fn
表示要执行的代码,可以是一个包含javascript代码的字符串,也可以是一个函数。第二个参数ms
是以毫秒表示的时间,表示fn
需推迟多长时间执行。clearTimeout()
方法用于停止执行setTimeout()
方法的函数代码。
let timer = setTimeout(function() {
console.log("50ms后输出");
}, 50);
//50ms后输出
上面这个函数表示的是在50毫秒后执行console.log("50ms后输出");
那么,问题来了,到底js是怎么处理setTimeout
函数的?这50ms之间又能发生什么?其实这要涉及到js的异步机制,setTimeout
就是一个异步处理的函数。那什么是异步处理呢?我们用另外一个函数来给大家演示一下:
(function () {
let timer = setTimeout(function() {
console.log("0");
}, 0);
console.log("1")
})();
我们把间隔时间设置0
,让他可以达到直接输出的效果,但是结果又是怎样呢?显然结果与我们想象的有些不一样,输出结果如下:
1
0
这就很奇怪了,明明应该先输出0的,输出的顺序却发生了改变。这就是因为setTimeout
是一个异步处理的函数。
因为js是单线程,所以js异步机制是,当js代码中碰到异步操作时,会先跳过该操作,先执行下面的代码,在异步操作完成后再执行。这样就很容易明白上面的输出顺序了。js碰到setTimeout()
后尽管其间隔为0
秒,还是会跳过该函数先往下执行,这样就会输出1
后再输出0
。从setTimeout()
角度看,该函数会被暂时挂起,等待时间完成后再加入到js的任务队列末中。所以我们就得到了上面的结果
那么是往下的代码全部执行完再回头执行该异步操作吗?我们可以用一个简单的函数作为实例,答案告诉我们这是对的,在其所在的作用域内,等其中词法环境中所有的代码都执行完毕后再回头执行该异步操作,原因是其达到设置时间后,被加入到了任务队列的末尾,因此在最后才执行该函数
(function () {
let timer = setTimeout(function() {
console.log("0");
}, 0);
for (let i = 1; i < 100; i++) {
console.log(i);
}
(function () {
for (let i = 1; i < 11; i++) {
console.log("1");
}
})();
})();
//0 1 2 3 .... 98 99 先完成循环中输出1-99
//1 1 1 1 1 1 1 1 1 1 再输出10个1
//0 最后输出0
ok,相信大家对异步函数也有一点儿了解了。那么重点来了,setTimeout()
既然是异步函数,那么如果想要实现这么一个想法:间隔3s
后再间隔2s
执行某个操作,应该怎么完成?或者说要同步处理连续性的setTimeout()
要怎么解决?
我们先来看看这样的代码能不能实现
(function () {
let timer1 = setTimeout(function() {
console.log("0");
}, 3000);
let timer2 = setTimeout(function() {
console.log("1");
}, 2000);
})();
//2s后输出1
//3s后输出0
显然,这是不可能实现的,因为setTimeout()
开始执行时会被挂起,然后等到时间结束后排到任务队列末。代码开始运行时,timer1
会被挂起,代码继续往下执行,遇到timer2
也被挂起。当然,timer2
间隔时间为2s
,比timer1
时间短,因此timer2
将会先加入到任务队列末,所以先执行timer2
输出1
。随后再过1s
,timer1
也到时间后再加入任务队列末,所以我们不难想到,按照这个按顺序执行下来。这整个函数的完成时间是3s
,而不是我们想要的5s
。
这个时候我介绍两种方法给大家解决:
1.使用回调函数
function time1(callback) {
let timer1 = setTimeout(function() {
console.log("0");
callback();
}, 3000);
}
time1(function () {
let timer2 = setTimeout(function() {
console.log("1");
}, 2000);
});
//0 3s后输出
//2 5s后输出
1方法变形,嵌套函数
function time1(callback) {
let timer1 = setTimeout(function() {
console.log("0");
let timer2 = setTimeout(function() { //直接将第二个timeout函数嵌套到第一个里面去
console.log("1");
}, 2000);
}, 3000);
}
//0 3s后输出
//2 5s后输出
2.使用递归函数
function time1() {
let timer1 = setTimeout(function() {
console.log("1");
time1();
}, 3000);
}
time1();
//1 每隔3s输出
第二种方法完成了setInterval()
的功能,实际上setInterval()
也是与setTimeout()
一样的机制。
那么到这里为止,我们就已经可以同步的去处理setTimeout()
函数了,达到我们想要的目的。当然处理异步方法还有迭代函数,不过在这里却不适用于setTimeout()
,因为他是按时间决定顺序。
最后,如果该文章有什么错误遗漏的地方欢迎指出,我会及时更正。大家在学习的同时我也在学习,谢谢大家。