第二十九章 同步异步编程 EventLoop事件循环机制的底层机制

125 阅读2分钟

ECMA规范JS

W3C规范浏览器(GUI)

一、前端开发中的同步异步编程

浏览器是多线程的,但是它只分配一个 “JS引擎线程” 用来渲染和解析JS代码,所以JS是单线程的!!

1、浏览器常用线程

  • GUI渲染线程
  • JS引擎线程
  • 事件触发线程
  • 定时器触发线程
  • 异步HTTP请求线程
  • WebWorker
  • ...

2、JS中大部分代码都是“同步编程”,例如:循环...

  • 千万不要写死循环,一旦遇到死循环,则JS引擎线程会一直被占用,
  • 遇到程序抛出异常,后边的代码也不会再执行了(throw new Error:手动抛出异常)
  • 我们可以基于( try{ } catch( err ){ //err就是我们捕获到的异常错误 } )进行异常捕获,这样不会影响后续代码的执行。
try {
    console.log(a); //Uncaught ReferenceError: a is not defined
    // throw new Error('xxx'); //手动抛出异常
} catch (err) {
    // err:我们捕获到的异常错误
}
console.log('ok'); 

2、JS中也存在“同步编程”:

  • 原理:依托于浏览器多线程,再次基于EventLoop事件循环机制处理的
  • 异步宏任务(macrotask)\
    • 定时器 setTimeout/setInterval\
    • 事件绑定\
    • Ajax/Fetch\
    • ...
  • 异步微任务(microtask)\
    • requestAnimationFrame\
    • Promise.then/catch/finally\
    • async/await\
    • queueMicrotask 基于这个方法可以创建一个异步微任务\
    • IntersectionObserver\
    • ...

二、EventLoop事件循环机制

  • console.time('标识');

//...程序

console.timeEnd('标识');

作用:记录程序执行所需要的时间「预估值」:受电脑此时的性能影响,每一次册数的值都不尽相同。

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
    // do soming
}
console.timeEnd('AA'); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);

  • 定时器到时间后也不一定能执行(设定的事件是其最快执行的时间):如果此时主线程被占用,则必须等到主线程空闲下来,排在EventQueue中的定时器才可以执行!\
    • 定时器等待时间不设置或者设置为0,不是立即执行,而是要等待浏览器的最快反应时间【谷歌:5-7ms】\
    • 基于JS和定时器实现动画效果\
    • 出现卡顿情况:到时间该走了,但是主线程被占用,它走不了\
    • 我们设定的时间很难和“屏幕刷新率”保持一致\
    • 屏幕刷新率:正常电脑屏幕刷新率60HZ(1秒刷新60次,大概16.66ms刷新一次)\
    • 基于window.requestAnimationFrame 实现动画\
    • 不需要我们设置时间,默认是按照电脑“屏幕刷新率对应的时间”进行运动的\
    • 虽然也会出现“因主线程被占用,它无法立即执行”导致的卡顿,但是比定时器好,因为他是异步微任务,优先于异步宏任务执行!