本文很长,列举的情况很多。
在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践。
每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~
setInterval:表示多久执行一次,需要clearInterval(timer)来让它停下。要是不clearInterval(timer),它就会越来越快!
setTimeout:表示过了多久之后执行,只会执行一次!
比如这段代码,实现的是每秒都在console里输出现在的时间,5秒后停止。
上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test</title>
</head>
<body>
<div id="root"></div>
<script> let timer timer = setInterval(function () {
console.log(new Date()) }, 1000) setTimeout(function () {
clearInterval(timer) }, 5000) </script>
</body>
</html>
运行结果:
如果setTimeout设置时间为0呢?
修改一下代码:
<script> let timer timer = setInterval(function () {
console.log(new Date()) }, 1000) setTimeout(function () {
clearInterval(timer) }, 5000) setTimeout(function () {
console.log('hello') }, 0) </script>
运行结果:
加上promise
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test</title>
</head>
<body>
<div id="root"></div>
<script> console.log('1') setTimeout(() => {
console.log('2') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') }, 0) console.log('4') }) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
</body>
</html>
看下结果:
这一段完全没有调用到。
promise.then((res) => {
console.log('5')
})
.then((res) => {
console.log('6')
})
为啥呢,因为没有resolve()
加上resolve()之后呢?
<script> console.log('1') setTimeout(() => {
console.log('2') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') setTimeout(() => {
resolve() }, 0) }, 0) console.log('4') }) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
那是不是什么时候resolve()就什么时候输出呢?
答案是:不是
把“2”换到下面来试一下:
<script> console.log('1') let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') setTimeout(() => {
resolve() }, 0) }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
可见3和2的位置确实变了,但是promise部分,就是5和6,仍然是最后出现。
那有多个setTimeout会怎样呢?我们加一个“2.5”试一下:
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') setTimeout(() => {
resolve() }, 0) }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
输出结果是:
可见是从上至下的啊~
如果让resolve()和console.log(‘3’)摆在一起呢?
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') resolve() }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
执行结果:
可见,在setTimeout里套的setTimeout会被最后执行。
如果改一下执行时间呢?把2.5输出的时间改为运行0.1s之后?
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 100) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') setTimeout(() => {
resolve() }, 0) }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
执行结果:
它会出现在最后!
那如果resolve()执行时间延后呢?
<script> console.log('1') let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') resolve() }, 100) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
运行结果:
该先执行的还是先执行了啊~
加上async/await会怎样?神奇的事发生了!
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') resolve() }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) async function async1() {
console.log('async1 start') await async2() console.log('async1 end') } async function async2() {
console.log('async2') } console.log('script start') async1() console.log('script end') promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
输出结果是:
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。
setTimeout会在async/await之后再执行。但promise的内容会在async/await之前执行。
混乱起来了:把setTimeout塞进async/await中会怎么样?
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') resolve() }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) async function async1() {
console.log('async1 start') setTimeout(() => {
console.log('async1 start setTimeout') }, 0) await async2() console.log('async1 end') setTimeout(() => {
console.log('async1 end setTimeout') }, 0) } async function async2() {
console.log('async2') setTimeout(() => {
console.log('async2 setTimeout') }, 0) setTimeout(() => {
console.log('async2 setTimeout after') }, 100) } console.log('script start') async1() console.log('script end') promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
输出结果:(看仔细了哦)
所以塞在async/await中的setTimeout会被最后执行。
终极混乱:
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3')
setTimeout(() => {
resolve()
}, 0)
}, 0)
console.log('4')
})
在Promise里的setTimeout里的setTimeout里的resolve()是什么时候执行的?
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test</title>
</head>
<body>
<div id="root"></div>
<script> console.log('1') setTimeout(() => {
console.log('2.5') }, 0) let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3') setTimeout(() => {
resolve() }, 0) }, 0) console.log('4') }) setTimeout(() => {
console.log('2') }, 0) async function async1() {
console.log('async1 start') setTimeout(() => {
console.log('async1 start setTimeout') }, 0) await async2() console.log('async1 end') setTimeout(() => {
console.log('async1 end setTimeout') }, 0) } async function async2() {
console.log('async2') setTimeout(() => {
console.log('async2 setTimeout') }, 0) setTimeout(() => {
console.log('async2 setTimeout after') }, 100) } console.log('script start') async1() console.log('script end') promise .then((res) => {
console.log('5') }) .then((res) => {
console.log('6') }) console.log('7') </script>
</body>
</html>
输出结果:
啊,原来是这样,这一部分会被最后执行!
(作者已经要陷入混乱中了…)
休息一下…接着做~
setTimeout的层叠实验:
<script> setTimeout(() => {
console.log('1') setTimeout(() => {
console.log('1.2.before3') setTimeout(() => {
console.log('1.3.1') }, 0) setTimeout(() => {
console.log('1.3.2') }, 0) }, 0) setTimeout(() => {
console.log('1.2.after3') }, 0) }, 0) setTimeout(() => {
console.log('2') setTimeout(() => {
console.log('2.2.1') }, 0) setTimeout(() => {
console.log('2.2.2') }, 0) }, 0) setTimeout(() => {
console.log('3') }, 0) </script>
输出结果:
事实证明,setTimeout是一层一层来的,理解好“第几批”很重要。
可以自己尝试一下,这个如金字塔一样的结构,先搭好第一层地基,才会向上搭建这种思想。
至此你已经看完了所有的内容,感谢观看,实操大于理解,还是建议读者新建一个项目自己动手尝试各种情况,还有更多嵌套情况本文没有列出,如有勤奋的同学列举,欢迎讨论~
补充:
setTimeout并不是准时的原因
- 因为 JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。
- 为了控制要执行的代码,就有一个 JavaScript 任务队列。
- 这些任务会按照将它们添加到队列的顺序执行。
- setTimeout() 的第二个参数告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行