继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

关于setTimeout()异步性

bb7bb
关注TA
已关注
手记 16
粉丝 175
获赞 978

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。随后再过1stimer1也到时间后再加入任务队列末,所以我们不难想到,按照这个按顺序执行下来。这整个函数的完成时间是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(),因为他是按时间决定顺序。

最后,如果该文章有什么错误遗漏的地方欢迎指出,我会及时更正。大家在学习的同时我也在学习,谢谢大家。

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP