本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、单线程异步理解
想象一个只有1个收银员的快餐店:
同步模式(愚蠢的做法)
- 顾客A点了一份现做的汉堡
- 收银员站在厨房等汉堡做好(10分钟)
- 这期间其他顾客全部干等着
- 汉堡好了才处理下一个顾客
→ 结果:队伍排到店外,顾客投诉
异步模式(聪明的做法)
- 顾客A点汉堡后,收银员说:"好了叫您,请旁边稍等"
- 立即处理顾客B的薯条订单(立即可取)
- 顾客C要可乐,直接给
- 汉堡做好了回调顾客A
→ 结果:虽然只有1个收银员,但处理效率极高
二、JavaScript的单线程异步原理
1. 核心三件套
部件 | 作用 | 快餐店类比 |
---|---|---|
调用栈 | 执行同步代码 | 收银员当前正在处理的任务 |
任务队列 | 存放待执行的回调 | 取餐区的已完成订单 |
事件循环 | 检查并执行回调 | 收银员不断查看取餐区的流程 |
2. 工作流程
console.log("点餐开始"); // 同步任务
// 异步任务(点汉堡)
setTimeout(() => {
console.log("汉堡好了");
}, 1000);
console.log("继续服务其他顾客"); // 同步任务
执行顺序:
- 打印"点餐开始"(同步)
- 把
setTimeout
回调注册到Web APIs,开始计时 - 打印"继续服务其他顾客"(同步)
- (1秒后)计时结束,回调进入任务队列
- 事件循环发现调用栈为空,执行回调
- 打印"汉堡好了"
三、鸿蒙的增强设计
鸿蒙在标准JS引擎上增加了多线程能力:
1. 三种线程类型
线程类型 | 作用 | 是否影响主线程 |
---|---|---|
UI线程 | 界面渲染/响应点击 | 主线程 |
TaskPool | 执行耗时计算 | 不影响 |
Worker | 长期后台任务 | 不影响 |
2. 场景示例
// 主线程(UI线程)
async function onButtonClick() {
// 1. 显示加载状态(主线程立即执行)
showLoading();
// 2. 使用TaskPool处理图片(异步)
const processedImage = await taskpool.execute(
new taskpool.Task(heavyImageProcess, imageData)
);
// 3. 更新UI(返回主线程)
updateImage(processedImage);
}
// 会被放到其他线程执行
@Concurrent
function heavyImageProcess(data: ArrayBuffer) {
// 复杂图像处理...
return processedData;
}
四、常见问题
1: 不是说JS单线程吗?怎么还能多任务?
- 单线程指:主线程同时只能做一件事
- 异步秘诀:把耗时操作委托给系统底层(不是JS线程做的)
- 比如文件读取是操作系统完成的
- 网络请求是网卡处理的
- 定时器是系统计时器管理的
2: await是不是让代码变同步了?
- 不是!这只是语法糖:
// 这两种写法完全等价
async function foo() {
const a = await bar(); // 看似"暂停"
console.log(a);
}
function foo() {
return bar().then(a => {
console.log(a);
});
}
3: 为什么动画卡死了?
典型错误:
function doWork() {
// 长耗时同步操作(阻塞主线程)
for(let i=0; i<1000000000; i++) {
heavyCalculation();
}
// 期间界面完全冻结
}
正确做法:
async function doWork() {
// 将任务分片放入任务队列
for(let i=0; i<100; i++) {
await taskpool.execute(new taskpool.Task(chunkWork, i));
}
}
五、加强理解
在浏览器中运行该代码,观察执行顺序:
console.log("开始");
setTimeout(() => console.log("定时器1"), 0);
Promise.resolve().then(() => console.log("微任务1"));
setTimeout(() => console.log("定时器2"), 0);
Promise.resolve().then(() => console.log("微任务2"));
console.log("结束");
/* 输出顺序:
开始
结束
微任务1
微任务2
定时器1
定时器2
*/
总结
- 单线程就像快餐店只有1个收银员。
- 异步就是让顾客"稍等取餐",不阻塞队伍。
- 回调/Promise是取餐时的叫号系统。
- 鸿蒙的TaskPool相当于开了后厨专用通道。
- 记住黄金法则:主线程只做轻量工作,重活交给其他线程。