浏览器工作原理与实践(四)

消息队列:单行道->先进先出。又被称为宏任务,执行宏任务的时候,如果DOM有变化,将变化提交到任务中。当宏任务执行完再执行微任务。优先级问题。
消息队列和事件循环类似于观察者发布订阅模式,事件循环(单线程运行过程中能接收并执行新任务)是按顺序执行消息队列中任务的。for
主线程——>消息队列。主线程——>消息队列——>消息队列中微任务。
消息队列中任务:解析DOM事件、重新布局事件、垃圾回收任务、IO任务、点击事件。
定时器需要在指定间隔才能调用回调函数。不能直接添加到消息队列,只能放在延迟队列。
消息队列执行完会执行延迟队列,嵌套调用最短时间为
setTimeout是将延迟任务添加到延迟队列中,XMLHttpRequest发起请求是由浏览器的网络进程去执行,再将结果利用IPC方式通过渲染进程再添加到消息队列。
页面中大部分任务都是在主线程执行的:

  1. 渲染事件(解析DOM、计算布局、绘制)
  2. 用户交互事件(鼠标点击、滚动页面、放大缩小等)
  3. JS脚本执行事件
  4. 网络请求完成,文件读写完成事件
    渲染进程内部会有多个消息队列,比如延迟和普通的消息队列。然后主线程采用for循环,不断从这些任务队列中取出任务并执行任务。消息队列中的任务叫宏任务,消息队列中的任务是通过事件循环来执行的。setTimeout的函数触发也都在宏任务。为了执行时间精度的控制,Promise执行放到微任务,当前执行完执行微任务然后再执行宏任务,所以比setTimout快。
    微任务中的微任务,在微任务执行之后,继续执行微任务。
    在当前宏任务中的JS快执行完成时,JS引擎准备退出全局执行上下文,并清空调用栈时,JS引擎会检查全局执行上下文的微任务队列,然后顺序执行微任务。微任务是V8引擎在创建全局执行上下文时创建的队列,当有微任务时再存放进去。
    监听DOM,MutationObserver将响应函数改成异步,等多次DOM变化后,合成一次触发异步调用,微任务通知变化,异步+微任务。
    Promise的出现是改变回调的编码风格,但它excutor和then里面用的还是回调函数。
    模拟Promise:
function Bromise(executor){ var onResolve=null; var onReject=null; this.then=function(onResolve,onReject){ onResolve=onResolve; } funtion resolve(value){ setTimeout(()=>{onResolve(value)},0) } executor(resolve,null) } 调用: function executor(resolve,reject){ resolve(100) }; let demo=new Bromise(executor); function onResolve(value){ console.log(value) } demo.then(onResolve);

Promise通过回调函数延迟绑定,回调函数返回值穿透和错误冒泡技术解决了多层嵌套和错误捕获。
Generator
生成器Generator函数是一个带星号的函数,可以暂停执行和恢复执行的。
协程是比线程更加轻量级的存在,一个线程上可以存在多个协程,但同期只能执行一个协程,如果从A协程启动B协程,A就是B的父协程,协程由用户控制执行。
function* genDemo(){ console.log('执行第一段'); yield 'generator1'; console.log('执行第二段'); yield 'generator2'; console.log('执行结束'); return 'generator3' } console.log('main0'); let gen=genDemo(); console.log(gen.next().value); console.log('main1'); console.log(gen.next().value); console.log('main2'); console.log(gen.next().value); console.log('main3');

输出结果全局和genDemo交替执行,生成器特性可以暂停,恢复执行。
  1. 生成器内部执行一段代码,遇到yield,JS引擎返回后面的内容给外部,并暂停
  2. 外部函数可以通过next恢复函数执行。(1)通过调用genDemo创建一个协程gen,创建后gen协程没有立即执行。(2)让gen协程执行,要通过调用gen.next()。(3)当协程正在执行的时候,通过yield来暂停gen协程的执行,并返回主要信息给父协程。(4)协程执行期,遇到return,JS引擎会结束当前协程,将return后面的内容返回给父协程。
    async
    【浏览器工作原理与实践(四)】async/await:使用了同步的方式写异步代码
    async function foo() { console.log(1) let a=await 100; console.log(a); console.log(2) } console.log(0) foo(); console.log(3)

  3. 执行console.log(0)打印0
  4. 执行foo,由于被async标记过,JS引擎会保存当前调用栈,执行console.log(1)
  5. 执行await 100,JS会创建一个promise,let promise=new Promise(resolve,reject){resolve(100)},JS引擎将任务提交给微任务队列promise。
  6. JS引擎暂停当前协程执行,将主线程控制权交给父协程,同时将promise对象返回给父协程
  7. 主线程控制权给过父协程,父协程要调promise.then来监控promise状态的改变
  8. 接下来继续父协程流程执行,执行console.log(3),父协程执行结束,结束之前进入微任务检查,执行微任务,resolve(100),resolve回调函数被激活后,将主线程控制权交给foo函数的协程,将value值传给该协程
  9. foo协程激活后,把value值给a,然后foo协程继续执行console.log(a),console.log(2)

    推荐阅读