阅读 460

JS事件循环机制(Eventloop)与其微任务宏任务

一、首先上代码

    console.log('script start');

    async function async1() {
        await async2()
        console.log('async1 end');
    }

    async function async2() {
        console.log('async2 end');
    }
    async1()

    setTimeout(function () {
        console.log('setTimeout');
    }, 0)

    new Promise(resolve => {
        console.log('Promise');
        resolve()
    })
        .then(function () {
            console.log('promise1');
        })
        .then(function () {
            console.log('promise2');
        })

    console.log('script end');  
复制代码

首先,看到这段代码,你们知道它的打印顺序是什么吗?而其为什么又会是这样的一个打印顺序呢,在捋清它的打印顺序之前,我们先来了解一些相关知识点。

二.什么是线程和进程

进程:描述了cpu在运行指令及加载和保存上下文所需要的时间、

线程:进程中更小的单位,描述的是一种指令所需要的时间可能导致不安全的渲染ui,得益于js是单线程运行的,可以达到节省运行内存,节约上下文切换的时间

相信大家应该都清楚,js是单线程,多个线程一起运行时会提高效率,而单线程就意味着,同一个时间只能做一件事情,当前一个任务执行完了,下一个任务才会执行,那么,就会有人问了,js为什么不能有多个线程呢,这样可以提高效率啊?JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。

三.JS单线程带来什么好处?

js引擎在js运行时会阻塞ui的渲染(渲染引擎的工作) ==> js殷勤线程和渲染线程是互斥的

因为js可以修改dom结构,如果在js执行的时候ui线程还在工作,就可能导致不安全的UI渲染,得益于js是单线程运行的,可以达到达到节省内存(运行内存),节约上下文切换的间。

四.setTimeout是如何实现的?

1.浏览器的页面是由消息队列和事件循环系统来驱动的

2.渲染线程中所有运行在主线程的任务都需要添加到消息队列

3.事件循环系统会按照顺序执行消息队列里面的任务

4.要执行一段异步代码需要首先将异步添加到消息队列,消息队列里面的任务是按照顺序执行的

代码中设定了一个 setTimeout,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。

五.Event-loop

1.microtask 微任务队列

异步回调:a.把异步回调函数封装成一个宏任务,添加到当循环系统执行到该任务的时候执行回调函数消息队列尾部,

b.执行时机是在主函数执行结束之后,当前宏任务结束之前执行回调函数,这通常以微任务的形式出现

微任务就是一个需要异步执行的函数,执行时机是在主函数执行之后,当前宏任务执行结束之前

微任务包括:process.nextTick,promise, promise.then, MutationObserver

2.macrotask 宏任务队列

宏任务包括:script, setTimeout,setInterval, setImmediate, I/O, ui 渲染

3.执行顺序

a.首先执行同步代码,这属于宏任务

b.当执行完所有的同步代码后,执行栈为空,去查询是否有异步代码需要执行

c.如果有异步代码,执行所有微任务

d.当执行完所有的微任务后,如果有必要会渲染页面

e.开始下一轮eventloop,执行宏任务中的异步代码

做好以上那些准备工作,接下来就可以利用这些知识点去理解最开始的那段代码了,那么你们的执行结果也是下面这个顺序吗?

六.执行结果

 script start
 async2 end
 Promise
 script end
 async1 end
 promise1
 promise2
 setTimeout
复制代码

其中,有两点要特别注意的是:

1.async await相当于promise.then,很多人以为await会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上await是一个让出线程的标志,会先执行await后的函数,所以第二个打印结果是'async2 end'.

2.Promise是一个特例,新版游览器对Promise有优先级,会让出线程让Promise先执行。所以在这里会先执行Promise,这并不代表Promise是一个同步函数,Promise一定是一个异步函数这是没错的。

文章分类
前端
文章标签