JS

setTimeout

Posted by weite122 on 2018-03-04

定义

  • setTimeout()方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。每次执行完后会自动清除,setInterval不会清除。

语法

1
var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...])
  • function
    • function 是你想要在delay毫秒之后执行的函数。
  • delay | 可选
    • 延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0。实际的延迟时间可能会比 delay 值长。
  • param1, …, paramN 可选
    • 附加参数,一旦定时器到期,它们会作为参数传递给function 或 执行字符串(setTimeout参数中的code)

使用

  1. 简单的调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fn() {
console.log(1)
}

setTimeout(fn) //正确
setTimeout(fn()) // 错误使用,传入的是函数,不是函数的返回值

function fn() {
return function fn2() {
return 3
}
}

fn()() //3
setTimeout(fn()) //这样调用就没问题
  1. timeId
  • code:
1
2
3
4
5
6
7
8
9
10
function fn() {
console.log(1)
}

var timerId = setTimeout(fn)
console.log('id' + timeId)
console.log(2)

timerId = setTimeout(fn)
console.log('id' + timeId)
  • 结果:

    深度截图_选择区域_20180304204714.png

  • 分析:
    • 每次执行 setTimeout都会有一个timeId,浏览器执行代码从上到下执行,当看到setTimeout会给他设置一个闹钟,然后给这个闹钟一个timeId和delay(默认是0),把setTimeout的函数放到后面执行,当执行完其他函数时,再去执行setTimeout的函数,当再setTimeout时,把之前的闹钟砸掉。setTimeout的timeId是唯一的,不会相同。
      深度截图_选择区域_20180304203846.png
  1. delay
  • code:
1
2
3
4
5
6
7
function fn() {
console.log(new Date())
console.log(1)
}

var timerId = setTimeout(fn)
console.log(new Date())
  • 结果:
  • delay此时是0,从结果上看好像没什么问题,但是我们如果换成时间戳看就会发现差别(时间戳是自 1970 年 1 月 1 日(00:00:00 GMT)以来的秒数)
    深度截图_选择区域_20180304210501.png
  • code:
1
2
3
4
5
6
7
function fn() {
console.log(new Date()-0)
console.log(1)
}

var timerId = setTimeout(fn)
console.log(new Date()-0)
  • 结果:
    • 测试的结果总是会有30~40ms的延时。当然,不同浏览器延时不同,这里我使用的是Chrome浏览器。浏览器在执行setTimeout之前总会有其他的代码需要执行,所以,尽管你延时设置0秒,还是会有几十毫秒的偏差。当浏览器最小化失去焦点时,你打印的时间可能会偏差一秒,因为浏览器默认节省内存消耗。
      深度截图_选择区域_20180304210411.png
  • 清除setTimeout

window.clearTimeout

1
2
3
4
5
6
7
8
9
function fn() {
console.log(1)
}

var timerId = setTimeout(fn)

window.clearTimeout(timerId, 2000)

var timerId = setTimeout('console.log("2秒了")')

异步与同步

  • 异步: 我不等待结果,使用回调处理

  • 同步: 我等待结果

  • 已春运买票为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function 用软件抢票(通知我) {
var timerId = setTimeout(function fn() {
通知我('100')
}, 1000)
return timerId
}
/*
var 我的票 = undefined

function 通知我(result) {
我的票 = result
console.log(我的票)
}
*/

function 通知我(result) {
console.log(result)
}
用软件抢票(通知我)

面试题

  • 以下代码的结果分别是什么?
1
2
3
for (var i = 0; i < 5; i ++) {
console.log(i)
}
1
2
3
4
5
for (var i = 0; i < 5; i ++) {
setTimeout(function(){
console.log(i)
}, 1000 * i)
}
1
2
3
4
5
6
for (var i = 0; i < 5; i ++) {
(function(i) {
setTimeout(function(){
console.log(i)
}, 1000 * i)})(i)
}
1
2
3
4
5
6
for (var i = 0; i < 5; i ++) {
(function() {
setTimeout(function(){
console.log(i)
}, 1000 * i)})(i)
}
1
2
3
4
5
for (var i = 0; i < 5; i ++) {
setTimeout((function(i) {
console.log(i)
})(i), 1000 * i)
}
  • 分析:

    1. 直接打出0,1,2,3,4
    2. 0秒后打印5,1秒后打印5,2秒后打印5,3秒后打印5,4秒后打印5(由于setTimeout的原因,console.log(i)都要在i*1000ms后执行,但此时for循环已经结束,i=5)
    3. 0秒后打印0,1秒后打印1,2秒后打印2,3秒后打印3,4秒后打印1(立即执行函数表达式保存变量i)
    • 拆分一下代码:
    1
    2
    3
    4
    5
    6
    7
    8
    for (var i = 0; i < 5; i ++) {
    var t1 = function(i) {
    setTimeout(function(){
    console.log(i)
    }, 1000 * i)
    }
    t2 = t1(i)//for循环每次i都保存了,如果i没有传入,结果就是5
    }
    1. 0秒后打印5,1秒后打印5,2秒后打印5,3秒后打印5,4秒后打印5
  1. 直接打出0,1,2,3,4
  • 拆分代码:
1
2
3
4
5
6
7
8
for (var i = 0; i < 5; i ++) {
var t1 = function () {
console.log(i)
}
var t2 = t1(i)//i每次都保存了,但是t1没有return,所以t2的值为undefined
var t3 = i * 1000
setTimeout(t2,t3)//t2为undefined,所以setTimeout什么都做不了,每次运行到t2时,自动打印出i,分别是0,1,2,3,4
}

制作一个倒计时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
.wrapper {
display: flex;
justify-content: center;
}

#outputTime {
margin: 0 atuo;
text-align: center;
}
</style>
</head>

<body>
<div class="wrapper">
<select placeholder="请选择时间" id="Countdown">
<option value="1">1分钟</option>
<option value="5">5分钟</option>
<option value="10">10分钟</option>
<option value="60">60分钟</option>
<option value="1000">1000分钟</option>
</select>
<button id="startButton">start</button>
<button id="pauseButton">pause</button>
<button id="resumeButton">resume</button>
</div>


<div id="outputTime">
</div>
<script>
let timeLeft
let lastTiemId

startButton.onclick = function() {
pauseButton.disabled = false
var timeValue = parseInt(Countdown.value, 10)
timeLeft = timeValue * 60
if (lastTiemId) {
window.clearTimeout(lastTiemId)
}
showTime()
resumeButton.disabled = true
}


function CountTime() {
var hours = Math.floor(timeLeft / (60 * 60))
var minutes = Math.floor(timeLeft / 60) - hours * 60
var seconds = timeLeft % 60
var s = seconds < 10 ? "0" + seconds : seconds
var m = minutes < 10 ? "0" + minutes : minutes
var h = hours < 10 ? "0" + hours : hours
showValue = h + ":" + m + ":" + s
}

function showTime() {
CountTime(timeLeft)
outputTime.textContent = showValue
if (timeLeft === 0) {
return
}
timeLeft -= 1
lastTiemId = setTimeout(showTime, 1000, timeLeft)
}

pauseButton.onclick = function() {
if (lastTiemId) {
window.clearTimeout(lastTiemId)
}
pauseButton.disabled = true
resumeButton.disabled = false
}
resumeButton.onclick = function() {
pauseButton.disabled = false
resumeButton.disabled = true
showTime()
}
</script>
</body>

</html>