同步与异步
同步
调用之后失去后果,再执行其余工作
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,定时 2s
console.log(3) 同第一步,打印 3
执行栈空,查看工作队列,不到 2s 还没有工作
2s 到,事件触发线程将工作增加进工作队列
循环查看就会发现有工作,将工作放入执行栈执行,打印 2
定时器可能存在的问题
-
定时工作可能不会按时执行
// 因为同步工作执行了 5s,所以 5s 后才打印 2 const 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); }