本文内容主要来自阮一峰网络日志
一、为什么JavaScript是单线程?
JavaScript这门语言最大的特点之一就是他的单线程,也就是说,在同一时间只能做一件事。
为什么不实行多线程
这其实跟他的用途有关。作为一种浏览器的地脚本语言,他的主要作用就是实现用户互动,以及操作DOM。这也就决定了它地单线程特性。假定js同时有两个线程,一个线程需要删除内容,一个线程需要增加内容,那就不知道以哪个为准。
所以,为了避免复杂性,单线程就成了js这门语言的核心特征,将来应该也不会改变。
为了利用多核CPU的计算能力,HTML5提出了Web Worker
标准,允许js脚本创建多个线程,但是子线程完全受主线程控制,且不得操作。所以本质这个新标准并没有改变js的单线程性质。
二. 任务队列
js的单线程就意味着所以的任务执行都需要排队。前一个任务执行完成后才能执行后一个任务。如果前面任务执行时间过长,那么后面的任务不得不一直等待。
于是设计者们将所有任务分为了两种,同步任务与异步任务。
同步任务指的是在主线程执行的任务,也就是需要等待前面任务执行完成才能执行后面任务的机制。
异步任务指的是不进入主线程,而进入任务队列,只有等待任务队列通知主线程某个任务可以执行了,该任务才会进入主线程。
具体异步执行机制如下:
- 所有同步任务都在主线程执行,形成一个执行栈。
- 主线程之外还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件。
- 一旦所有的同步任务执行完成后,系统就会读取任务队列,那些异步任务于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重读以上过程。
三. 计时器
使用计时器可以将代码手动放入任务队列里。
setTimeout(fn,0)
的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()
的效果要好于setTimeout()。
需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
网友评论