单线程的JavaScript如何管理任务

386 阅读4分钟

同步任务和异步任务

JavaScript中的任务分为:同步任务和异步任务

同步任务:在主线程上排队执行的任务,前一个任务完整的地执行完成后,后一个任务才会被执行

异步任务:不会阻塞主线程,在其任务执行完成之后,会根据一定的规则去执行相关的回调

同步任务与函数调用栈

JavaScript在执行过程中每进入一个不同的运行环境,都会创建一个相应的执行上下文。那么当我们执行一段JavaScript代码时,通常会创建多个执行上下文。而JavaScript解释器会以栈的方式管理这些执行上下文,以及函数之间的调用关系,形成函数调用栈(调用栈可理解为一个存储函数调用的栈结构,遵循先进后出的原则)

JavaScript中代码的执行过程如下:

1.首先进入全局环境,全局执行上下文被创建并添加进栈中

2.每调用一个函数,该函数执行上下文会被添加进调用栈,并开始执行

3.如果正在调用栈中执行的A函数还调用了B函数,那么B函数也将会被添加进调用栈

4.一旦B函数被调用,便会立即执行

5.当前函数执行完毕后,JavaScript解释器将其清除调用栈,继续执行当前执行环境下的剩余代码

异步任务和回调队列

异步任务包括一些需要等待响应的任务,包括用户交互、HTTP请求、定时器等。使用异步任务,JavaScript线程就不用处于等待状态,CPU也可以处理其他任务

异步任务需要提供回调函数,当异步任务有了运行结果之后,该任务则会被添加到回调队列中,主线程在适当的时候会从回调队列中取出相应的回调函数并执行。

JavaScript在运行的时候,除了函数调用栈之外,还包含一个待处理的回调队列。在回调队列中的都是已经有了运行结果的异步任务,每个异步任务都会关联一个回调函数。回调队列遵循的是先进先出的原则

单线程的JavaScript是如何管理任务的

JavaScript有一个基于事件循环的并发模型,称为事件循环,它的设计解决了并发任务和异步任务的管理问题

EventLoop负责执行代码、收集和处理事件以及执行队列中的子任务,具体包括以下过程:

1.JavaScript有一个主线程和调用栈,所有的任务最终都会被放到调用栈等待主线程执行

2.同步任务会被放到调用栈中,按照顺序等待主线程依次执行

3.主线程之外存在一个回调队列,回调队列中的异步任务最终会在主线程中以调用栈的方式运行

4.同步任务都在主线程上执行,栈中代码在执行的时候会调用浏览器的API,此时会产生一些异步任务

5.异步任务会在有了结果后,将异步任务以及关联的回调函数放入回调队列中

6.调用栈中任务执行完毕后,此时主线程处于空闲状态,会从回调队列中获取任务进行处理

上述过程不断重复,这就是JavaScript的运行机制,称为事件循环机制(EventLoop)

宏任务和微任务

事件循环中的异步回调队列有两种:宏任务和微任务

宏任务:包括script全部代码、setTimeout、setInterval、setImmediate、I/O操作,UI渲染

微任务:包括process.nextTick、Promise

为什么将异步任务分为宏任务和微任务呢?是为了避免回调队列中等待执行的宏任务过多,导致某些微任务的等待时间过长。在每个宏任务执行完成之后,会先将微任务队列中的任务执行完毕,再执行下一个宏任务

在浏览器的异步回调队列中,宏任务和微任务的执行过程如下:

1.宏任务队列一次只从队列中取一个任务执行,执行完成后就去执行微任务队列中的任务

2.微任务队列中的所有任务都会被依次取出来执行,直到微任务队列为空

3.在执行完所有的微任务之后,执行下一个宏任务之前,浏览器会执行UI渲染操作,更新界面