同步与异步
同步
调用之后得到结果,再执行其他任务
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); }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。