异步原理
1. 线程与进程
- 进程: 程序实例,CPU->进程,除非切换上下文或多核
- 线程: 进程的某个任务,共享进程资源
- 单线程: JS 存在大量互斥操作,注定单线程(DOM)
2. 单线程
- 概念: 同一时刻,只能执行一个任务
- 优点: 按用户要求顺序执行,结果可控
- 缺点: 如果有耗时任务,必然阻塞整个线程
- 方案: 耗时任务异步执行,其它同步执行
3. 同步与异步
- 同步: 顺序,静态可控,但耗时阻塞
- 异步: 乱序,动态,不阻塞但不可控
- 同步任务: 在主线程(函数栈)中执行
- 异步任务: 脱离主线程执行(浏览器/Node)
4. 异步任务
- 定时任务: setTimeout(),setInterval()
- 事件监听: addEventListener()
- 网络请求: ajax,promise,fetch
- IO 操作: 文件,数据库…
5. 事件循环
- 前提: JS 所有任务必须在主线程(函数栈)中执行
- 任务队列: 异步任务准备就绪后在此排队等待
- 事件循环: 将任务队列中就绪异步任务调入主线程执行
- 调用栈空: 主线程所有任务(同步/异步)执行完毕
只有同步任务才进入主线程执行,所以此时异步任务已转换成同步任务
异步原理HTML示范:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>异步任务演示</title>
</head>
<body>
<script>
// 1. 三个同步任务
console.log(1);
console.log(2);
console.log(3);
console.log('--------');
// 2. 2个同步任务,1个异步任务
console.log(1);
setTimeout(() => console.log(2), 1000);
console.log(3);
console.log('---------');
</script>
<!-- DOM渲染是同步,还是异步? 同步-->
<!-- keyup: 抬起时触发 -->
<input type="text" onkeyup="console.log(this.value)" placeholder="keyup" />
<!--
keyup: 实现输入与控制台输出同步实时显示
1. 当抬起时,输入框中内容已渲染完成,同步任务结束
2. 异步任务开始: keyup已获取到输入框内容,显示正确
-->
<br />
<!-- keydown: 按下时触发 -->
<input type="text" onkeydown="setTimeout(()=>console.log(this.value),0)" placeholder="keydown" />
<br />
<!--
setTimeout加上后,你按下某个键之后,keydown也会立即执行,但执行的是一个异步任务
而这个异步任务并不会马上执行,而是要待当前同步任务完成(当前同步任务是DOM渲染)
待主线程同步任务执行完毕,setTimeout()最低延迟立即执行,从而成功的跳过了空字符
实现了和keyup一样的实时同步显示效果
-->
<input type="text" oninput="console.log(this.value)" placeholder="input" />
<!-- input === keyup === setTimeout(()=>keydown) 实际上完全不同 -->
<!-- keyup,keydown: 键盘事件
input: 文本事件 -->
<!-- 思考: 当输入内容时, keyup,keydown,input 的顺序是什么? -->
<!-- keydown -> input -> keyup -->
</body>
</html>