相信大家经常会听到一句话,就是 “JS是单线程的”,可是什么是 线程,什么又是 单线程,有 多线程 吗?
讲到线程,那么肯定也得说一下进程。其实在本质上,两个名词都是 CPU 工作时间片的一个描述。
进程(process) 指的是CPU 在 运行指令及加载和保存上下文所需的时间,放在应用上是指计算机中已运行的程序。
线程(thread) 是操作系统能够进行运算的最小单位。它被包含在 进程 之中,描述了执行一段指令所需的时间。
浏览器中的线程分了以下几类:
执行栈可以理解为是用来存储函数调用的栈,遵循先进后出的原则。
Node 的 Event Loop 分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。
Event Loop 6 个阶段:
浏览器端 的情况与 node端 的情况相仿,当我们执行 JS 代码的时候其实就是往执行栈中放入函数,当遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行。
执行顺序如下:
上面讲了定时器是属于 宏任务(macrotask) 。如果当前 执行栈 所花费的时间大于 定时器 时间,那么定时器的回调在 宏任务(macrotask) 里,来不及去调用,所有这个时间会有误差。
我们看以下代码:
setTimeout(function () { console.log('biubiu');}, 1000);某个执行时间很长的函数();
如果定时器下面的函数执行要 5秒钟,那么定时器里的log 则需要 5秒之后再大圆,函数占用了当前 执行栈,要等执行栈执行完毕后再去读取 微任务(microtask),等 微任务(microtask) 完成,这个时候才会去读取 宏任务(macrotask) 里面的 setTimeout 回调函数执行。setInterval 同理,例如每3秒放入宏任务,也要等到执行栈的完成。
还有一种情况如下:
setTimeout(function() { console.log('嘤嘤嘤');}, 0);
因为 定时器 最小 delay 是 4毫秒,所以小于这个数字,即使 执行栈 已空,依然不会按设置好的 delay 去执行。