同步与异步
同步
调用之后失去后果,再执行其余工作
const test = () => { let t = +new Date(); while (true) { if (+new Date() - t >= 2000) { break; } }};console.log(1);test();console.log(2);console.log(3);// 输入:1 // 执行test()占用2s,2s后输入 2 3
异步
调用之后不论后果,继续执行其余工作
console.log(1);setTimeout(() => { console.log(2);}, 2000);console.log(3);// 输入:1 3// 定时器2秒后输入 2
过程与线程
材料:过程与线程的简略解释
过程
程序运行的实例同一程序能够产生多个过程一个过程蕴含一个或多个线程
线程
操作系统可能进行运算调度的最小单位一次只能执行一个工作有本人的调用栈、寄存器环境同一过程的线程共享过程资源
查看过程的罕用linux命令:
查看过程的状态:`ps (process status)`查看动静的过程变动:`top (table of processes)`
JS单线程
浏览器的过程
启动浏览器后,会产生多个过程
材料:
古代浏览器的多过程架构
浏览器的过程和线程
渲染过程
渲染过程包含:
- GUI线程 渲染布局,解析HTML、CSS,构建DOM树
JS引擎线程
- 解析执行JS(Chrome V8就是JS引擎,跑在JS引擎线程,JS引擎线程只有一个。因为解释JS的引擎是单线程的,所以称JS为单线程)
- 与GUI线程互斥(因为JS能够操作DOM,如果和GUI线程同时操作DOM,就会出问题)
定时器触发线程
- setTimeout
- setInterval
事件触发线程
- 将满足条件的事件放入工作队列
异步HTTP申请线程
- XHR所在线程 (解决ajax申请,申请实现后,如果有回调就告诉事件触发线程往工作队列增加事件)
JS通过浏览器内核的多线程实现异步
异步场景:
- 定时器
- 网络申请
- 事件绑定
- Promise
定时器
定时器工作流程
- 调用WebAPI,如setTimeout
- 定时器线程计数
- 事件触发线程将定时器事件放入工作队列
- 主线程通过Event Loop遍历工作队列
代码剖析:
console.log(1);setTimeout(() => { console.log(2);}, 2000);console.log(3);
执行过程:console.log(1) 同步工作入栈执行,执行完出栈,打印1调用setTimeout,定时2sconsole.log(3) 同第一步,打印3执行栈空,查看工作队列,不到2s还没有工作2s到,事件触发线程将工作增加进工作队列循环查看就会发现有工作,将工作放入执行栈执行,打印2
定时器可能存在的问题
定时工作可能不会按时执行
// 因为同步工作执行了5s,所以5s后才打印2const test = () => { let t = +new Date(); while (true) { if (+new Date() - t >= 5000) { break; } }};setTimeout(() => { console.log(2);}, 2000);test();
- 定时器嵌套5次后最小距离不能低于4ms(不同浏览器实现不同)
定时器利用场景
- 防抖
- 节流
- 倒计时
- 动画(存在丢帧的状况)学习requestAnimationFrame
setTimeout代码执行剖析
for (var i = 1; i <= 10; i++) { setTimeout(function() { console.log(i); }, 1000 * i);}
setTimeout等同步的for循环执行后才执行,此时i曾经变成11,所以后果是每隔1s打印一个11,打印10个11
能够利用闭包造成作用域,保留i的值,能够实现打印每隔1s打印1-10
for (var i = 1; i <= 10; i++) { (function (x) { setTimeout(() => { console.log(x) }, 1000 * x) })(i) }
因为var没有块级作用域,所以换成ES6的let也能够实现
for (let i = 1; i <= 10; i++) { setTimeout(function() { console.log(i); }, 1000 * i);}