一直以来对于JS线程中很多概念总是模糊的概念,很多人会把JS线程的概念和微任务、宏任务分开来理解,其实本身他们就是一件完整的事情.
JS一直以来是单线程运行,所以在实际环境中,由于settimeout,DOM的一些操作事件,异步都会有单独的线程来辅助JS线程的运行

其中浏览器有很多线程,例如:

  1. GUI 渲染线程
  2. JS 引擎线程
  3. 定时器触发线程 (setTimeout)
  4. 浏览器事件线程 (onclick)
  5. http 异步线程
  6. EventLoop轮询处理线程

其中1、2、4为常驻线程

一个进程的运行,当然需要很多个线程互相配合,当然一个网页能够正常的运行并和用户交互,也需要很多个进程之间相互配合,而其主要的一些线程,刚才在上面已经列出来了,分类:

  • 类别A:GUI 渲染线程
  • 类别B:JS 引擎线程
  • 类别C:EventLoop轮询处理线程
  • 类别D:其他线程,有 定时器触发线程 (setTimeout)、http 异步线程、浏览器事件线程 (onclick)等等。


类别B:

JS主线程,主要运行一些非异步的代码

var a=1;
console.log(a)

setTimeout(function(){
console.log(2)
})

其中:

var a =1;
console.log(a);

都是运行在JS主线程之中


类别D

JS代码中,碰到异步代码,就被放入相对应的线程中去执行,例如

1 var a = 2;
2 setTimeout(fun A)
3 ajax(fun B)
4 console.log()
5 dom.onclick(func C)

主线程在运行这段代码时,碰到2 setTimeout(fun A),把这行代码交给定时器触发线程去执行
碰到3 ajax(fun B),把这行代码交给http 异步线程去执行
碰到5 dom.onclick(func C) ,把这行代码交给浏览器事件线程去执行
注意: 这几个异步代码的回调函数fun A,fun B,fun C,各自的线程都会保存着的,因为需要在未来执行啊。。。
所以,这几个线程主要干两件事:

  • 执行主线程扔过来的异步代码,并执行代码
  • 保存着回调函数,异步代码执行成功后,通知EventLoop轮询处理线程过来取相应的回调函数


消息队列(任务队列)

可以理解为一个静态的队列存储结构,非线程,只做存储,里面存的是一堆异步成功后的回调函数,为根据实际执行的顺序依次仍进队列中

注意:

  1. 是异步成功后,才把其回调函数扔进队列中,而不是一开始就把所有异步的回调函数扔进队列。比如setTimeout 3秒后执行一个函数,那么这个函数是在3秒后才进队列的。
  2. 当两个执行结果同时要进入列队时,这里就牵扯到了微任务和宏任务的说法,我们会在一会针对微任务和宏任务特别说明


类别C

EventLoop轮询处理线程

上面我们已经知道了,有3个东西

  1. 主线程,处理同步代码
  2. 类别D的线程,处理异步代码
  3. 消息队列,存储着异步成功后的回调函数,一个静态存储结构

在一开始,消息队列是空的,当我们拿到回调函数后,会依次按照顺序存储在消息列队里,但是我们的JS主线程,异步线程和消息列队是不能直接联系的,这里就需要一个中间介质EventLoop来与他们三个沟通

从主线程那里顺时针看,整个的流程是循环往复的
upload successful



微任务、宏任务

再从我们的所有异步线程执行后的回调函数进入消息列队的时候,如果不同时间触发,那么会按照时间顺序依次进入,假如在同一时刻,不同的回调函数进入,这时就会出现先后顺序,而这里始终遵循的原则:先微任务,后宏任务

setTimeout(() => console.log(4))

new Promise(resolve => {
resolve()
console.log(1)
}).then(() => {
console.log(3)
Promise.resolve().then(() => {
console.log('before timeout')
}).then(() => {
Promise.resolve().then(() => {
console.log('also before timeout')
})
})
})

console.log(2)

结果

1
2
3
before timeout
also before timeout
4

宏任务

# 浏览器 node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame


微任务

# 浏览器 node
process.nextTick
MutationObserver
Promise.then catch finally