文章目录
- 一、js线程
- 1. 进程与线程区别?JS 单线程带来的好处?
- 2. 同步和异步函数
- 2.1 同步函数:函数返回--->拿到预期结果
- 2.2 异步函数:函数返回--->拿不到预期结果,还要通过一定的手段获得结果
- 异步过程:
- 异步类型:
- 二、事件循环
- 1. 什么是执行栈
- 爆栈
- 2. 消息队列
- 三、事件循环
- 四、 异步代码执行顺序?
- 1. 异步任务的分类?
- 五、Event Loop (事件循环)执行顺序如下所示:
一、js线程
之所以js中有事件循环,原因就是因为js是单线程的原因
1. 进程与线程区别?JS 单线程带来的好处?
一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁。
在 JS 运行的时候可能会阻止 UI 渲染,这说明了两个线程是互斥的。这其中的原因是因为 JS 可以修改 DOM,如果在 JS 执行的时候 UI 线程还在工作,就可能导致不能安全的渲染 UI。这其实也是一个单线程的好处,得益于 JS 是单线程运行的,可以达到节省内存,节约上下文切换时间,没有锁的问题的好处。
对于锁的问题,形象的来说就是当我读取一个数字 15 的时候,同时有两个操作对数字进行了加减,这时候结果就出现了错误。解决这个问题也不难,只需要在读取的时候加锁,直到读取完毕之前都不能进行写入操作。
2. 同步和异步函数
2.1 同步函数:函数返回—>拿到预期结果
Math.sqrt(2)
console.log('Hi')
2.2 异步函数:函数返回—>拿不到预期结果,还要通过一定的手段获得结果
fs.readFile('foo.txt', 'utf8', function(err, data){
console.log(data)
}
//该函数需要在全部文件都读取完之后才能console
异步过程:
- 注册函数:发起异步过程
- 回调函数:处理结果
异步类型:
- 普通事件:click、resize
- 资源加载:load、error
- 定时器:setInterval、setTimeout
二、事件循环
1. 什么是执行栈
可以把执行栈认为是一个存储同步函数调用的栈结构,遵循先进后出的原则。
当开始执行 JS 代码时,首先会执行一个 main 函数,然后执行我们的代码。根据先进后出的原则,后执行的函数会先弹出栈,在图中我们也可以发现,foo 函数后执行,当执行完毕后就从栈中弹出了。
function foo() {
throw new Error('error')
}
function bar() {
foo()
}
bar()
可以在上图清晰的看到报错在 foo 函数,foo 函数又是在 bar 函数中调用的。
爆栈
当我们使用递归的时候,因为栈可存放的函数是有限制的,一旦存放了过多的函数且没有得到释放的话,就会出现爆栈的问题
function bar() {
bar()
}
bar()
2. 消息队列
对于同步任务,按照顺序既可,但是根据异步函数执行时间长短不一样,所以就有了消息队列
消息指的是–>注册异步任务时添加的回调函数
js遇到异步函数时,不会一直等待其结果,而是挂起,继续执行执行栈中的任务;只要异步操作执行完成,就可以到消息队列中去排队,然后主线程空闲的时候,就可以从消息队列中获取消息并执行其回调
三、事件循环
以一个代码为例:
div.onclick = () => {
console.log('hi!')
}
- js引擎解析到该段代码时,将onclick这个函数挂起
- 如果点击了这个div,就是异步操作执行完成,那么就将这个函数放入消息队列中
- 当执行栈处于闲置状态时,就从消息队列中取出该任务对应的回调放进执行栈中执行
(其实以上描述就属于事件循环)
四、 异步代码执行顺序?
再次重复一下:
当遇到异步的代码时,会被挂起并在异步操作完成时加入到 消息队列中。一旦执行栈为空,js引擎就会从 消息 队列中拿出该异步函数的回调并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。
1. 异步任务的分类?
不同的任务源会被分配到不同的 消息队列中,任务源可以分为 微任务(microtask) 和 宏任务
- 宏任务:由宿主对象发起的任务(setTimeout)
- 宏任务包括
script
,setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
。
- 微任务:由js引擎发起的任务(promise)
- 微任务包括
process.nextTick
,promise
,MutationObserver
。
宿主对象:
由js 宿主环境提供的对象,他们的行为完全由宿主环境决定
- 固有(new image 创建img 元素)
- 用户可创建的(document.createElement就可以创建Dom)
五、Event Loop (事件循环)执行顺序如下所示:
- 在异步事件执行完操作后会放入一个执行队列里,而根据这个异步事件的类型会被放入对应的宏任务队列或者微任务队列中
- 当执行栈为空时,主线程会先去执行微任务队列中对应的回调函数,再去执行宏任务队列中的任务(当然是取出放入执行栈中执行)
- 在一次循环中,微任务永远在宏任务之前执行