js线程执行原理

2019-09-30  本文已影响0人  木中木

        众所周知,浏览器执行JS是单线程的,当然除了最新的worker线程,worker线程号称是浏览器端执行复杂运算一种解决方案,可以与主线程并行运算,而不互相排斥。浏览器线程主要分为以下三种:

1.就是我们熟悉的js线程,也就是js主引擎线程,执行js主线程代码。由于浏览器是单线程,所以浏览器无论什么时候都是一个线程在运行,这个线程也一直等待执行任务

2.GUI线程,执行dom树和css树回流和重绘。这里引起回流和重绘原因有很多种,都是由这个线程去处理,这里要注意的是GUI和js主引擎线程是互斥的,所以通常情况下,js引擎线程执行时间不能过长,过长就会引起GUI线程等待时间过长,页面就会造成卡顿。

3.事件触发线程,用于触发注册以及事件回调和异步代码执行。我们编写的异步代码(事件触发程序和settimeout),js主引擎执行到这些代码时,会先扔给事件触发线程,事件触发线程里面建立一个队列,按照先进先出的原则,依次把异步代码扔进队列,等待主线程空闲之后,再从触发线程队列中依次取出。

4.事件循环线程,用于管理我们的异步代码,存放到一个线程池中

    那我们熟悉这四个线程之后,我们就可以稍微明白js异步代码的执行原理,首先js异步代码并不是真正的异步代码,伪异步执行,只有在JS线程空闲的时候,才会去取监听线程代码程序。下面,我们阐述一下setTimeout执行原理。

比如:

`

console.log('+++++++++')

setTimeout(()=>{

    console.log('执行了异步代码');

},0);

console.log('==============')

`

主线程程序,按照顺序执行,遇到setTimeout,则是交给事件触发线程,由于时间是0,所以立即进去队列,然后主线程再去执行下面的代码,所以依次打印 ++++++++, =======,等到主线程空闲了,然后从事件循环线程中取出异步代码执行程序,执行settimeout,之后再打印'执行了异步代码'。

下面我们来看下一段代码

`

function threadDemo() {

    console.log(1);

    setTimeout(() => {

        console.log(2);

        Promise.resolve().then(() => {

            console.log(3)

        });

    }, 0);

    new Promise((resolve, reject) => {

        console.log(4)

        resolve(5)

    }).then((data) => {

        console.log(data);

    })

    Promise.resolve().then(() => {

        console.log('8');

    });

    setTimeout(() => {

        console.log(6);

    }, 0)

    console.log(7);

}

threadDemo();

`

如果你能知道最后的运行结果,就知道后面我们要讲述的内容。

js异步任务分为macrotask和mincrotask,这两个任务都是在主线程的某个时机放入任务队列中。

宏任务Macrotask, 在浏览器端,其可以理解为该任务执行完后,在下一个macrotask执行开始前,浏览器可以进行页面渲染:

    setTimeout / setInterval / setImmediate / IO / render / postMessage / script整体代码都是属于宏任务。

微任务mincrotask,可以理解为在macrotask任务执行后,页面渲染前立即执行的任务:

    promise.then / process.nexttick / MutationObserver 

微任务的执行优先级高于宏任务,每个宏任务执行完成后,都会清空微任务队列。

比如,我们执行一段script代码里面含有微任务,那这个执行过程是这样的。

首先,执行整体script代码,我们可以视为宏任务,执行过程中,如果遇到settimeout此类的宏任务就会把宏任务加入到任务执行队列中,遇到微任务则加入到微任务执行队列中。

然后,整体script宏任务执行完成后,就会开始执行微任务队列,如果这个过程中微任务又产生了微任务,则继续加到微任务队列的末位,直至整个微任务执行完成。

其次,微任务执行完成后,就开始UI线程的渲染。

最后,UI线程渲染完成后,js主线程又会继续到任务队列中取宏任务,重复上述步骤。

所以上述代码的执行结果是:1,4,7,5,8,2,3,6

上一篇下一篇

猜你喜欢

热点阅读