--------------------- 浏览器的页面循环系统 ----------------
一、事件的循环系统
1、要想在线程运行过程中,能够接受并执行新的任务,就需要采用事件循环机制
2、如何设计好一个线程模型,能让其能够接受其他线程发送的消息呢?
①一个通用的模式是 - 消息队列 --主线程从消息队列中取任务,其他线程给消息队列塞任务
3、处理其他进程发送过来的任务
渲染进程专门有一个IO线程用来接收其他进程传进来的消息--接收消息之后,会将这些消息组装成任务-发送给渲染主线程
4、安全退出主线程
每次执行完一个任务后,会判断是否有设置退出标志
5、总结:
①对于已经确定好的任务,可以使用一个单线程来按照顺序处理这些任务
②若在线程执行过程中接收并处理新的任务,就需要引入循环语句和事件系统
③如果要接收其他线程发送过来的任务,需要引入消息队列
④如果其他进程要想发送任务给页面主线程,那么先通过IPC把任务发送给渲染进程的IO线程,IO线程再把任务发送给 页面主线程
⑤消息队列机制并不是太灵活,为了适应效率和实时性,引入了微任务
每个宏认为都有一个微任务表,对于临时安排的任务放入微任务表,当宏任务完成后,继续执行微任务
总之,浏览器页面是由消息队列 和事件循环系统来驱动的
消息队列和主线程循环机制保证了页面有条不紊的运行
二、setTimeout是怎么实现的
它就是一个定时器,用来指定某个函数在多少毫秒之后执行
1、为了支持定时器的实现,浏览器增加了延时队列
当执行完当前任务后,去执行到期的延迟任务(eg:SetTimeout任务)
2、其次,由于消息队列排队和一些系统级别的限制,通过setTimeout设置的
回调任务并非总是可以实时地被执行
①当前任务很耗时
②嵌套调用时,系统会设置最短时间间隔为4s(超过5层)
③未激活的页面,setTimeOut最小间隔为1000ms
④setTimeOut设置回调函数的this是指window,可以使用箭头函数解决
⑤延时执行做大值为2147483647,溢出会导致定时器立即执行
三、XMLHttpRequest
1、把一个函数作为参数传递给另外一个函数,那作为参数的这个函数就是回调函数
2、同步回调:callback是在主函数返回之前执行的
3、回调函数在主函数 外部执行的过程称为异步回调 ,有两种方式
①把异步函数 做成一个任务,添加到消息队列尾部
②把异步函数添加到微任务队列中,这样就可以在当前任务的末尾处执行微任务了
XMLHttpRequest(获取服务器数据,局部更新页面) 发起请求,是由浏览器的其他进程或者线程去执行,然后再将执行的结果利用IPC的方式通知渲染进程,之后渲染进程再将对应的消息添加到消息队列中。
四、宏任务与微任务
产生微任务的两种方式 :
① 使用MutationObserver 监控某个DOM节点,然后再通过JavaScript 来修改这个节点,或者为这个节点添加、删除部分子节点,当DOM节点发生变化时,就会产生DOM变化记录的微任务
②使用Promise, 当调用Promise.resolve()或者Promise.reject()的时候,也会产生微任务
五、Promise
1、解决的问题 -- 异步编码风格的问题
六、async/ await 使同步的方式去写异步代码
1、因为Promise的编程模型充斥着大量的then方法,虽然解决了嵌套回调问题,
但是在语义 方面仍然存在缺陷,这就是async/await 出现的原因
2、使用async/ await可以实现用同步代码的风格来编写异步代码,这是因为saync/await的基础基础使用了生成器和Promise, 生成器是协程的实现,利用生成器可以实现生成器函数的暂停和恢复