页面循环系统
js是单线程的,事件循环是js实现异步的一种方法,是js的执行机制,是一个不断进行循环的机制,来寻找可以执行的任务来执行。页面循环系统是浏览器统一管理页面中任务的机制,浏览器执行的任务,分为宏任务和微任务,宏任务在宏任务队列里,常见的宏任务:包括整体代码script(同步宏任务)、 setTimeout、setInterval(异步宏任务)、I/O、UI 交互事件(优先级较高)、postMessage、MessageChannel、setImmediate(Node.js 环境)。微任务在微任务队列里,常见的微任务: Promise.then(),process.nextTick,ajax请求(异步微任务)。 微任务的执行时机:执行时机是在主函数执行结束之后、当前宏任务结束之前。每个宏任务都关联了一个微任务队列。async/await是微任务+协程来实现的。
第三版线程:在第二版的线程模型中,所有的任务都是来自于线程内部的,如果其它线程想让主线程执行一个任务,利用第二版的线程模型是无法做到的。线程间通信:添加消息队列。渲染进程中所有运行在主线程上的任务都需要先添加到消息队列,然后事件循环系统再按照顺序执行消息队列中的任务。由于是多个线程操作同一个消息队列,所以在添加任务和取出任务时还会加上一个同步锁。
不过刚提到的线程仅限于同一个进程内的,不同进程的线程通信:如渲染进程专门有一个 IO 线程用来接收其他进程传进来的消息,接收到消息之后,会将这些消息组装成任务发送给渲染主线程,后续的步骤就和前面的“处理其他线程发送的任务”一样了(循环机制+事件+消息队列)
当页面主线程执行完成之后,又该如何保证页面主线程能够安全退出呢?Chrome 是这样解决的,确定要退出当前页面时,页面主线程会设置一个退出标志的变量,在每次执行完一个任务时,判断是否有设置退出标志。
页面使用单线程的缺点:
第一个问题是如何处理高优先级的任务,(如 DOM 发生变化,采用同步通知的方式,会影响当前任务的执行效率;如果采用异步方式,又会影响到监控的实时性。)(消息队列机制不是很灵活所以引入了微任务);
第二个是如何解决单个任务执行时长过久的问题(js回调、异步)。
宏任务与微任务:
这两篇文章力荐!!!!!
深入前端-彻底搞懂JS的运行机制
这一次彻底弄懂JavaScript执行机制
消息队列不能胜任部分领域的要求(如监听dom变化),而微任务可以在实时性和效率之间做一个有效的权衡。
微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。每个宏任务都关联了一个微任务队列。
举个例子更好理解:
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{//
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0)
//微任务Promise1进微任务队列
//注册宏任务setTimeout1,进入宏任务队列
//先执行微任务Promise1,打印'Promise1',遇到宏任务setTimeout2,进入宏任务队列,此时队列前面是setTimeout1
//接着执行宏任务setTimeout1,打印‘setTimeout1',遇到微任务Promise2,进入微任务队列
//此时宏任务里剩一个setTimeout2,微任务队列里剩一个Promise2,先执行微任务,再执行宏任务。
//Promise1 setTimeout1 Promise2 setTimeout2
- 微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列。
- 微任务的执行时长会影响到当前宏任务的时长。比如一个宏任务在执行过程中,产生了 100 个微任务,执行每个微任务的时间是 10 毫秒,那么执行这 100 个微任务的时间就是 1000 毫秒,也可以说这 100个微任务让宏任务的执行时间延长了 1000 毫秒。
- 在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行。
setTimeout和XMLHttpRequest是两个浏览器内置的WebAPI:
setTimeout
js——setTimeout和setInterval
XMLHtttpRequest
js_Ajax