js之运行机制


js的异步和单线程

js的单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

js异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
console.log(1);
setTimeout(function () {
console.log(2);
}, 1000);
console.log(3); // 1,3,2

console.log(1);
setTimeout(function () {
console.log(2);
}, 0); // 将1000改为0
console.log(3); // 1,3,2

console.log('A');
setTimeout(function () {
console.log('B');
})
while (1) {
} // A

// 总结:js 是单线程(同一时间只能做一件事),而且有一个任务队列:全部的同步任务执行完毕后,再来执行异步任务。第一行代码和最后一行代码是同步任务;但是,setTimeout是异步任务。
执行的顺序:1、先执行同步任务。2、遇到异步任务setTimeout,要挂起。3、全部的同步任务执行完毕后,再来执行异步任务。
js的同步
1
2
3
console.log('A');
alert('haha');
console.log('B'); // 只有点击确定了,才会打印B
同步和异步的对比

因为setTimeout是异步任务,所以程序并不会卡在那里,而是继续向下执行(即使settimeout设置了倒计时一万秒);但是alert函数是同步任务,程序会卡在那里,如果它没有执行,后面的也不会执行(卡在那里,自然也就造成了阻塞)。

setTimeout和setInterval的两个方法

对于第二个参数有以下需要注意的地方:1、当第二个参数缺省时,默认为 0;2、当指定的值小于 4 毫秒,则增加到 4ms(4ms 是 HTML5 标准指定的,对于 2010 年及之前的浏览器则是 10ms);也就是说至少需要4毫秒,该setTimeout()拿到任务队列中。

H5 Web Workers(多线程)

JavaScript是单线程。当一个页面加载一个复杂运算的 js 文件时,用户界面可能会短暂地“冻结”,不能再做其他操作。Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<input type="text" placeholder="数值" id="number">
<button id="btn">计算</button>
<script type="text/javascript">
// 1 1 2 3 5 8 f(n) = f(n-1) + f(n-2)
function fibonacci(n) {
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //递归调用
}
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
var result = fibonacci(number)
alert(result)
}
</script>
Web Workers的基本使用
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
// 新建一个 Worker 线程
var worker = new Worker('work.js');
// 主线程调用worker.postMessage()方法,向 Worker 发消息。
// 接着,主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。

var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
//创建一个Worker对象
var worker = new Worker('worker.js')
// 绑定接收消息的监听
worker.onmessage = function (event) {
console.log('主线程接收分线程返回的数据: '+event.data)
alert(event.data)
}
// 向分线程发送消息
worker.postMessage(number)
console.log('主线程向分线程发送数据: '+number)
}
console.log(this) // window

// Worker 线程内部需要有一个监听函数,监听message事件。
// 通过 postMessage(data) 方法来向主线程发送数据。
//worker.js文件
function fibonacci(n) {
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //递归调用
}
console.log(this)//[object DedicatedWorkerGlobalScope]
this.onmessage = function (event) {
var number = event.data
console.log('分线程接收到主线程发送的数据: '+number)
//计算
var result = fibonacci(number)
postMessage(result)
console.log('分线程向主线程返回数据: '+result)
// alert(result) alert是window的方法, 在分线程不能调用
// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
}